Start using readonly memory

This commit is contained in:
christian
2025-10-22 11:35:56 +02:00
parent d74d83453f
commit b477ddbe60
15 changed files with 124 additions and 74 deletions
+3 -2
View File
@@ -1,5 +1,6 @@
#if DEBUG #if DEBUG
using System.Globalization;
using MQTTnet.Internal; using MQTTnet.Internal;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
@@ -52,7 +53,7 @@ public sealed class Load_Tests : BaseTestClass
for (var j = 0; j < 1000; j++) for (var j = 0; j < 1000; j++)
{ {
publishPacket.Topic = j.ToString(); publishPacket.Topic = j.ToString(CultureInfo.InvariantCulture);
await client.SendAsync(publishPacket, CancellationToken.None); await client.SendAsync(publishPacket, CancellationToken.None);
} }
@@ -139,7 +140,7 @@ public sealed class Load_Tests : BaseTestClass
for (var j = 0; j < 1000; j++) for (var j = 0; j < 1000; j++)
{ {
var message = applicationMessageBuilder.WithTopic(j.ToString()).Build(); var message = applicationMessageBuilder.WithTopic(j.ToString(CultureInfo.InvariantCulture)).Build();
await client.PublishAsync(message); await client.PublishAsync(message);
} }
@@ -63,7 +63,7 @@ public static class MqttClientConnectResultFactory
RetainAvailable = connAckPacket.RetainAvailable, RetainAvailable = connAckPacket.RetainAvailable,
AssignedClientIdentifier = connAckPacket.AssignedClientIdentifier, AssignedClientIdentifier = connAckPacket.AssignedClientIdentifier,
AuthenticationMethod = connAckPacket.AuthenticationMethod, AuthenticationMethod = connAckPacket.AuthenticationMethod,
AuthenticationData = connAckPacket.AuthenticationData, AuthenticationData = connAckPacket.AuthenticationData.ToArray(),
MaximumPacketSize = connAckPacket.MaximumPacketSize, MaximumPacketSize = connAckPacket.MaximumPacketSize,
ReasonString = connAckPacket.ReasonString, ReasonString = connAckPacket.ReasonString,
ReceiveMaximum = connAckPacket.ReceiveMaximum, ReceiveMaximum = connAckPacket.ReceiveMaximum,
@@ -21,7 +21,7 @@ public static class MqttApplicationMessageFactory
Dup = publishPacket.Dup, Dup = publishPacket.Dup,
ResponseTopic = publishPacket.ResponseTopic, ResponseTopic = publishPacket.ResponseTopic,
ContentType = publishPacket.ContentType, ContentType = publishPacket.ContentType,
CorrelationData = publishPacket.CorrelationData, CorrelationData = publishPacket.CorrelationData.ToArray(),
MessageExpiryInterval = publishPacket.MessageExpiryInterval, MessageExpiryInterval = publishPacket.MessageExpiryInterval,
SubscriptionIdentifiers = publishPacket.SubscriptionIdentifiers, SubscriptionIdentifiers = publishPacket.SubscriptionIdentifiers,
TopicAlias = publishPacket.TopicAlias, TopicAlias = publishPacket.TopicAlias,
+1 -1
View File
@@ -58,7 +58,7 @@ public sealed class MqttBufferReader
return value; return value;
} }
public byte[] ReadRemainingData() public ReadOnlyMemory<byte> ReadRemainingData()
{ {
var bufferLength = BytesLeft; var bufferLength = BytesLeft;
if (bufferLength == 0) if (bufferLength == 0)
+86 -40
View File
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
@@ -108,31 +110,47 @@ public sealed class MqttBufferWriter
WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length); WriteBinary(propertyWriter._buffer, 0, propertyWriter.Length);
} }
public void WriteBinary(byte[] value) public void WriteBinary(ReadOnlySequence<byte> value)
{ {
if (value == null || value.Length == 0) if (value.IsEmpty)
{ {
EnsureAdditionalCapacity(2); WriteEmptyBinary();
return;
_buffer[_position] = 0;
_buffer[_position + 1] = 0;
IncreasePosition(2);
} }
else
EnsureAdditionalCapacity(value.Length + 2);
_buffer[_position] = (byte)(value.Length >> 8);
_buffer[_position + 1] = (byte)value.Length;
Advance(2);
foreach (var segment in value)
{ {
var valueLength = value.Length; MqttMemoryHelper.Copy(segment, 0, _buffer, _position + 2, segment.Length);
Advance(segment.Length);
EnsureAdditionalCapacity(valueLength + 2);
_buffer[_position] = (byte)(valueLength >> 8);
_buffer[_position + 1] = (byte)valueLength;
MqttMemoryHelper.Copy(value, 0, _buffer, _position + 2, valueLength);
IncreasePosition(valueLength + 2);
} }
} }
public void WriteBinary(ReadOnlyMemory<byte> value)
{
if (value.Length == 0)
{
WriteEmptyBinary();
return;
}
EnsureAdditionalCapacity(value.Length + 2);
_buffer[_position] = (byte)(value.Length >> 8);
_buffer[_position + 1] = (byte)value.Length;
Advance(2);
MqttMemoryHelper.Copy(value, 0, _buffer, _position + 2, (int)value.Length);
Advance(value.Length);
}
public void WriteBinary(byte[] buffer, int offset, int count) public void WriteBinary(byte[] buffer, int offset, int count)
{ {
ArgumentNullException.ThrowIfNull(buffer); ArgumentNullException.ThrowIfNull(buffer);
@@ -145,15 +163,7 @@ public sealed class MqttBufferWriter
EnsureAdditionalCapacity(count); EnsureAdditionalCapacity(count);
MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count); MqttMemoryHelper.Copy(buffer, offset, _buffer, _position, count);
IncreasePosition(count); Advance(count);
}
public void WriteByte(byte @byte)
{
EnsureAdditionalCapacity(1);
_buffer[_position] = @byte;
IncreasePosition(1);
} }
public void WriteString(string value) public void WriteString(string value)
@@ -165,7 +175,7 @@ public sealed class MqttBufferWriter
_buffer[_position] = 0; _buffer[_position] = 0;
_buffer[_position + 1] = 0; _buffer[_position + 1] = 0;
IncreasePosition(2); Advance(2);
} }
else else
{ {
@@ -189,26 +199,52 @@ public sealed class MqttBufferWriter
_buffer[_position] = (byte)(writtenBytes >> 8); _buffer[_position] = (byte)(writtenBytes >> 8);
_buffer[_position + 1] = (byte)writtenBytes; _buffer[_position + 1] = (byte)writtenBytes;
IncreasePosition(writtenBytes + 2); Advance(writtenBytes + 2);
} }
} }
public void WriteByte(byte @byte)
{
const int size = sizeof(byte);
var span = GetSpan(size);
span[0] = @byte;
Advance(size);
}
public void WriteTwoByteInteger(ushort value) public void WriteTwoByteInteger(ushort value)
{ {
EnsureAdditionalCapacity(2); const int size = sizeof(ushort);
var span = GetSpan(size);
_buffer[_position] = (byte)(value >> 8); BinaryPrimitives.WriteUInt16BigEndian(span, value);
IncreasePosition(1);
_buffer[_position] = (byte)value; Advance(size);
IncreasePosition(1);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Span<byte> GetSpan(int size)
{
var freeSpace = _buffer.Length - _position;
if (freeSpace < size)
{
EnsureCapacity(_buffer.Length + size - freeSpace);
}
return _buffer.AsSpan(_position, size);
}
public void WriteVariableByteInteger(uint value) public void WriteVariableByteInteger(uint value)
{ {
if (value == 0) if (value == 0)
{ {
_buffer[_position] = 0; _buffer[_position] = 0;
IncreasePosition(1); Advance(1);
return; return;
} }
@@ -216,7 +252,7 @@ public sealed class MqttBufferWriter
if (value <= 127) if (value <= 127)
{ {
_buffer[_position] = (byte)value; _buffer[_position] = (byte)value;
IncreasePosition(1); Advance(1);
return; return;
} }
@@ -238,11 +274,11 @@ public sealed class MqttBufferWriter
size++; size++;
} while (x > 0); } while (x > 0);
IncreasePosition(size); Advance(size);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
void EnsureAdditionalCapacity(int additionalCapacity) void EnsureAdditionalCapacity(long additionalCapacity)
{ {
var bufferLength = _buffer.Length; var bufferLength = _buffer.Length;
@@ -256,7 +292,7 @@ public sealed class MqttBufferWriter
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
void EnsureCapacity(int capacity) void EnsureCapacity(long capacity)
{ {
var newBufferLength = _buffer.Length; var newBufferLength = _buffer.Length;
@@ -275,7 +311,7 @@ public sealed class MqttBufferWriter
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
void IncreasePosition(int length) void Advance(int length)
{ {
_position += length; _position += length;
@@ -286,4 +322,14 @@ public sealed class MqttBufferWriter
Length = _position; Length = _position;
} }
} }
void WriteEmptyBinary()
{
EnsureAdditionalCapacity(2);
_buffer[_position] = 0;
_buffer[_position + 1] = 0;
Advance(2);
}
} }
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Buffers;
using MQTTnet.Packets; using MQTTnet.Packets;
namespace MQTTnet.Formatter; namespace MQTTnet.Formatter;
@@ -40,7 +41,7 @@ public static class MqttConnectPacketFactory
connectPacket.WillFlag = true; connectPacket.WillFlag = true;
connectPacket.WillTopic = clientOptions.WillTopic; connectPacket.WillTopic = clientOptions.WillTopic;
connectPacket.WillQoS = clientOptions.WillQualityOfServiceLevel; connectPacket.WillQoS = clientOptions.WillQualityOfServiceLevel;
connectPacket.WillMessage = clientOptions.WillPayload; connectPacket.WillMessage = new ReadOnlySequence<byte>(clientOptions.WillPayload);
connectPacket.WillRetain = clientOptions.WillRetain; connectPacket.WillRetain = clientOptions.WillRetain;
connectPacket.WillDelayInterval = clientOptions.WillDelayInterval; connectPacket.WillDelayInterval = clientOptions.WillDelayInterval;
connectPacket.WillContentType = clientOptions.WillContentType; connectPacket.WillContentType = clientOptions.WillContentType;
@@ -201,7 +201,7 @@ public sealed class MqttV3PacketFormatter : IMqttPacketFormatter
packet.WillRetain = willRetain; packet.WillRetain = willRetain;
packet.WillTopic = _bufferReader.ReadString(); packet.WillTopic = _bufferReader.ReadString();
packet.WillMessage = _bufferReader.ReadBinaryData(); packet.WillMessage = new ReadOnlySequence<byte>(_bufferReader.ReadBinaryData());
} }
if (usernameFlag) if (usernameFlag)
@@ -271,7 +271,7 @@ public sealed class MqttV3PacketFormatter : IMqttPacketFormatter
if (!_bufferReader.EndOfStream) if (!_bufferReader.EndOfStream)
{ {
packet.PayloadSegment = new ArraySegment<byte>(_bufferReader.ReadRemainingData()); packet.Payload = new ReadOnlySequence<byte>(_bufferReader.ReadRemainingData());
} }
return packet; return packet;
@@ -430,12 +430,12 @@ public sealed class MqttV3PacketFormatter : IMqttPacketFormatter
} }
} }
if (packet.Password != null && packet.Username == null) if (packet.Password.Length > 0 && packet.Username == null)
{ {
throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22]."); throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
} }
if (packet.Password != null) if (packet.Password.Length > 0)
{ {
connectFlags |= 0x40; connectFlags |= 0x40;
} }
@@ -460,7 +460,7 @@ public sealed class MqttV3PacketFormatter : IMqttPacketFormatter
bufferWriter.WriteString(packet.Username); bufferWriter.WriteString(packet.Username);
} }
if (packet.Password != null) if (packet.Password.Length > 0)
{ {
bufferWriter.WriteBinary(packet.Password); bufferWriter.WriteBinary(packet.Password);
} }
@@ -501,12 +501,12 @@ public sealed class MqttV3PacketFormatter : IMqttPacketFormatter
} }
} }
if (packet.Password != null && packet.Username == null) if (packet.Password.Length > 0 && packet.Username == null)
{ {
throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22]."); throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
} }
if (packet.Password != null) if (packet.Password.Length > 0)
{ {
connectFlags |= 0x40; connectFlags |= 0x40;
} }
@@ -531,7 +531,7 @@ public sealed class MqttV3PacketFormatter : IMqttPacketFormatter
bufferWriter.WriteString(packet.Username); bufferWriter.WriteString(packet.Username);
} }
if (packet.Password != null) if (packet.Password.Length > 0)
{ {
bufferWriter.WriteBinary(packet.Password); bufferWriter.WriteBinary(packet.Password);
} }
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Buffers;
using MQTTnet.Adapter; using MQTTnet.Adapter;
using MQTTnet.Exceptions; using MQTTnet.Exceptions;
using MQTTnet.Packets; using MQTTnet.Packets;
@@ -314,7 +315,7 @@ public sealed class MqttV5PacketDecoder
} }
packet.WillTopic = _bufferReader.ReadString(); packet.WillTopic = _bufferReader.ReadString();
packet.WillMessage = _bufferReader.ReadBinaryData(); packet.WillMessage = new ReadOnlySequence<byte>(_bufferReader.ReadBinaryData());
packet.WillUserProperties = willPropertiesReader.CollectedUserProperties; packet.WillUserProperties = willPropertiesReader.CollectedUserProperties;
} }
@@ -521,7 +522,7 @@ public sealed class MqttV5PacketDecoder
if (!_bufferReader.EndOfStream) if (!_bufferReader.EndOfStream)
{ {
packet.PayloadSegment = new ArraySegment<byte>(_bufferReader.ReadRemainingData()); packet.Payload = new ReadOnlySequence<byte>(_bufferReader.ReadBinaryData());
} }
return packet; return packet;
@@ -138,12 +138,12 @@ public sealed class MqttV5PacketEncoder(MqttBufferWriter bufferWriter)
} }
} }
if (packet.Password != null && packet.Username == null) if (packet.Password.Length > 0 && packet.Username == null)
{ {
throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22]."); throw new MqttProtocolViolationException("If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].");
} }
if (packet.Password != null) if (packet.Password.Length > 0)
{ {
connectFlags |= 0x40; connectFlags |= 0x40;
} }
@@ -193,7 +193,7 @@ public sealed class MqttV5PacketEncoder(MqttBufferWriter bufferWriter)
_bufferWriter.WriteString(packet.Username); _bufferWriter.WriteString(packet.Username);
} }
if (packet.Password != null) if (packet.Password.Length > 0)
{ {
_bufferWriter.WriteBinary(packet.Password); _bufferWriter.WriteBinary(packet.Password);
} }
@@ -29,7 +29,7 @@ public sealed class MqttV5PropertiesWriter
Write(MqttPropertyId.AssignedClientIdentifier, value); Write(MqttPropertyId.AssignedClientIdentifier, value);
} }
public void WriteAuthenticationData(byte[] value) public void WriteAuthenticationData(ReadOnlyMemory<byte> value)
{ {
Write(MqttPropertyId.AuthenticationData, value); Write(MqttPropertyId.AuthenticationData, value);
} }
@@ -44,7 +44,7 @@ public sealed class MqttV5PropertiesWriter
Write(MqttPropertyId.ContentType, value); Write(MqttPropertyId.ContentType, value);
} }
public void WriteCorrelationData(byte[] value) public void WriteCorrelationData(ReadOnlyMemory<byte> value)
{ {
Write(MqttPropertyId.CorrelationData, value); Write(MqttPropertyId.CorrelationData, value);
} }
@@ -315,9 +315,9 @@ public sealed class MqttV5PropertiesWriter
_bufferWriter.WriteString(value); _bufferWriter.WriteString(value);
} }
void Write(MqttPropertyId id, byte[] value) void Write(MqttPropertyId id, ReadOnlyMemory<byte> value)
{ {
if (value == null) if (value.Length == 0)
{ {
return; return;
} }
@@ -26,7 +26,7 @@ public class MqttEnhancedAuthenticationEventArgs : EventArgs
/// Gets the authentication data. /// Gets the authentication data.
/// Hint: MQTT 5 feature only. /// Hint: MQTT 5 feature only.
/// </summary> /// </summary>
public byte[] AuthenticationData => _initialAuthPacket.AuthenticationData; public byte[] AuthenticationData => _initialAuthPacket.AuthenticationData.ToArray();
/// <summary> /// <summary>
/// Gets the authentication method. /// Gets the authentication method.
@@ -67,7 +67,7 @@ public class MqttEnhancedAuthenticationEventArgs : EventArgs
{ {
return new ReceiveMqttEnhancedAuthenticationDataResult return new ReceiveMqttEnhancedAuthenticationDataResult
{ {
AuthenticationData = authPacket.AuthenticationData, AuthenticationData = authPacket.AuthenticationData.ToArray(),
AuthenticationMethod = authPacket.AuthenticationMethod, AuthenticationMethod = authPacket.AuthenticationMethod,
ReasonString = authPacket.ReasonString, ReasonString = authPacket.ReasonString,
ReasonCode = authPacket.ReasonCode, ReasonCode = authPacket.ReasonCode,
+1 -1
View File
@@ -9,7 +9,7 @@ namespace MQTTnet.Packets;
/// <summary>Added in MQTTv5.0.0.</summary> /// <summary>Added in MQTTv5.0.0.</summary>
public sealed class MqttAuthPacket : MqttPacket public sealed class MqttAuthPacket : MqttPacket
{ {
public byte[] AuthenticationData { get; set; } public ReadOnlyMemory<byte> AuthenticationData { get; set; }
public string AuthenticationMethod { get; set; } public string AuthenticationMethod { get; set; }
+1 -1
View File
@@ -13,7 +13,7 @@ public sealed class MqttConnAckPacket : MqttPacket
/// </summary> /// </summary>
public string AssignedClientIdentifier { get; set; } public string AssignedClientIdentifier { get; set; }
public byte[] AuthenticationData { get; set; } public ReadOnlyMemory<byte> AuthenticationData { get; set; }
public string AuthenticationMethod { get; set; } public string AuthenticationMethod { get; set; }
+6 -5
View File
@@ -2,13 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Buffers;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Packets; namespace MQTTnet.Packets;
public sealed class MqttConnectPacket : MqttPacket public sealed class MqttConnectPacket : MqttPacket
{ {
public byte[] AuthenticationData { get; set; } public ReadOnlyMemory<byte> AuthenticationData { get; set; }
public string AuthenticationMethod { get; set; } public string AuthenticationMethod { get; set; }
@@ -19,13 +20,13 @@ public sealed class MqttConnectPacket : MqttPacket
public string ClientId { get; set; } public string ClientId { get; set; }
public byte[] WillCorrelationData { get; set; } public ReadOnlyMemory<byte> WillCorrelationData { get; set; }
public ushort KeepAlivePeriod { get; set; } public ushort KeepAlivePeriod { get; set; }
public uint MaximumPacketSize { get; set; } public uint MaximumPacketSize { get; set; }
public byte[] Password { get; set; } public ReadOnlyMemory<byte> Password { get; set; }
public ushort ReceiveMaximum { get; set; } public ushort ReceiveMaximum { get; set; }
@@ -49,7 +50,7 @@ public sealed class MqttConnectPacket : MqttPacket
public bool WillFlag { get; set; } public bool WillFlag { get; set; }
public byte[] WillMessage { get; set; } public ReadOnlySequence<byte> WillMessage { get; set; }
public uint WillMessageExpiryInterval { get; set; } public uint WillMessageExpiryInterval { get; set; }
@@ -69,7 +70,7 @@ public sealed class MqttConnectPacket : MqttPacket
{ {
var passwordText = string.Empty; var passwordText = string.Empty;
if (Password != null) if (Password.Length > 0)
{ {
passwordText = "****"; passwordText = "****";
} }
+2 -2
View File
@@ -11,7 +11,7 @@ public sealed class MqttPublishPacket : MqttPacketWithIdentifier
{ {
public string ContentType { get; set; } public string ContentType { get; set; }
public byte[] CorrelationData { get; set; } public ReadOnlyMemory<byte> CorrelationData { get; set; }
public bool Dup { get; set; } public bool Dup { get; set; }
@@ -19,7 +19,7 @@ public sealed class MqttPublishPacket : MqttPacketWithIdentifier
public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; } = MqttPayloadFormatIndicator.Unspecified; public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; } = MqttPayloadFormatIndicator.Unspecified;
public ArraySegment<byte> PayloadSegment { set => Payload = new ReadOnlySequence<byte>(value); } //public ArraySegment<byte> PayloadSegment { set => Payload = new ReadOnlySequence<byte>(value); }
public ReadOnlySequence<byte> Payload { get; set; } public ReadOnlySequence<byte> Payload { get; set; }