Skip to content

Commit

Permalink
Refactored PrimitiveConverterProvider for less allocations
Browse files Browse the repository at this point in the history
Added CharConverter
  • Loading branch information
mcatanzariti committed Apr 7, 2024
1 parent c13c945 commit 26190f7
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 26 deletions.
9 changes: 9 additions & 0 deletions src/Dahomey.Cbor.Tests/CborReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,5 +377,14 @@ public void ReadNull(string hexBuffer, bool expectedValue, Type expectedExceptio
{
Helper.TestRead(nameof(CborReader.ReadNull), hexBuffer, expectedValue, expectedExceptionType);
}

[Theory]
[InlineData("62C3A9", 'é', null)] // "à"
[InlineData("6165", 'e', null)] // "a"
[InlineData("626162", ' ', typeof(CborException))] // "ab"
public void ReadChar(string hexBuffer, char expectedValue, Type expectedExceptionType)
{
Helper.TestRead(nameof(CborReader.ReadChar), hexBuffer, expectedValue, expectedExceptionType);
}
}
}
8 changes: 8 additions & 0 deletions src/Dahomey.Cbor.Tests/CborWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,5 +324,13 @@ public void WriteDataTypeCastAsObject(object value, string hexBuffer)
{
Helper.TestWrite(value, hexBuffer);
}

[Theory]
[InlineData("62C3A9", 'é')] // "à"
[InlineData("6165", 'e')] // "a"
public void WriteChar(string hexBuffer, char value)
{
Helper.TestWrite(nameof(CborWriter.WriteChar), value, hexBuffer, null);
}
}
}
26 changes: 24 additions & 2 deletions src/Dahomey.Cbor/Serialization/CborReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,28 @@ public sbyte ReadSByte()
return Encoding.UTF8.GetString(ReadSizeAndBytes(allowScratchBuffer: true));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public char ReadChar()
{
ReadOnlySpan<byte> bytes = ReadRawString();
char result;
unsafe
{
fixed (byte* rawBytes = bytes)
{
try
{
Encoding.UTF8.GetChars(rawBytes, bytes.Length, &result, 1);
}
catch (ArgumentException)
{
throw new CborException("Cannot read single char from buffer");
}
}
}
return result;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> ReadRawString()
{
Expand Down Expand Up @@ -368,7 +390,7 @@ public Half ReadHalf()
return (Half)ReadInteger();

case CborMajorType.NegativeInteger:
return (Half)(- 1L - (long)ReadInteger());
return (Half)(-1L - (long)ReadInteger());

case CborMajorType.TextString:
ReadOnlySpan<byte> buffer = ReadSizeAndBytes(true);
Expand Down Expand Up @@ -880,7 +902,7 @@ private void SkipSemanticTag()
public void SkipDataItem()
{
SkipSemanticTag();

CborReaderHeader header = GetHeader();

switch (header.MajorType)
Expand Down
17 changes: 17 additions & 0 deletions src/Dahomey.Cbor/Serialization/CborWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,23 @@ public void WriteString(ReadOnlySpan<byte> value)
_bufferWriter.Write(value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteChar(char value)
{
Span<byte> bytes = stackalloc byte[4];
int len;
unsafe
{
fixed (byte* rawBytes = bytes)
{
len = Encoding.UTF8.GetBytes(&value, 1, rawBytes, 4);
ReadOnlySpan<byte> exactBytes = bytes.Slice(0, len);
WriteInteger(CborMajorType.TextString, (ulong)len);
_bufferWriter.Write(exactBytes);
}
}
}

/// <summary>
/// Write the string header with a given size.
/// This leaves the writer in an invalid state and must be accompanied with a write to <see cref="BufferWriter"/> with exactly <paramref name="size"/> bytes.
Expand Down
15 changes: 15 additions & 0 deletions src/Dahomey.Cbor/Serialization/CharConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Dahomey.Cbor.Serialization.Converters
{
public class CharConverter : CborConverterBase<char>
{
public override char Read(ref CborReader reader)
{
return reader.ReadChar();
}

public override void Write(ref CborWriter writer, char value)
{
writer.WriteChar(value);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,54 @@
using Dahomey.Cbor.ObjectModel;
using System;
using System.Collections.Generic;

namespace Dahomey.Cbor.Serialization.Converters.Providers
{
public class PrimitiveConverterProvider : CborConverterProviderBase
{
private static readonly Dictionary<Type, Lazy<ICborConverter>> _lazyConverterTypes = new()
{
[typeof(bool)] = new(() => new BooleanConverter()),
[typeof(sbyte)] = new(() => new SByteConverter()),
[typeof(byte)] = new(() => new ByteConverter()),
[typeof(short)] = new(() => new Int16Converter()),
[typeof(ushort)] = new(() => new UInt16Converter()),
[typeof(int)] = new(() => new Int32Converter()),
[typeof(uint)] = new(() => new UInt32Converter()),
[typeof(long)] = new(() => new Int64Converter()),
[typeof(ulong)] = new(() => new UInt64Converter()),
[typeof(float)] = new(() => new SingleConverter()),
[typeof(double)] = new(() => new DoubleConverter()),
[typeof(decimal)] = new(() => new DecimalConverter()),
[typeof(string)] = new(() => new StringConverter()),
[typeof(ReadOnlyMemory<byte>)] = new(() => new ReadOnlyMemoryConverter()),
[typeof(byte[])] = new(() => new ByteArrayConverter()),
};

public override ICborConverter? GetConverter(Type type, CborOptions options)
{
if (_lazyConverterTypes.TryGetValue(type, out var converterType))
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean:
return new BooleanConverter();
case TypeCode.Byte:
return new ByteConverter();
case TypeCode.Char:
return new CharConverter();
case TypeCode.DateTime:
return new DateTimeConverter(options);
case TypeCode.Decimal:
return new DecimalConverter();
case TypeCode.Double:
return new DoubleConverter();
case TypeCode.Int16:
return new Int16Converter();
case TypeCode.Int32:
return new Int32Converter();
case TypeCode.Int64:
return new Int64Converter();
case TypeCode.SByte:
return new SByteConverter();
case TypeCode.Single:
return new SingleConverter();
case TypeCode.String:
return new StringConverter();
case TypeCode.UInt16:
return new UInt16Converter();
case TypeCode.UInt32:
return new UInt32Converter();
case TypeCode.UInt64:
return new UInt64Converter();
}

if (type == typeof(ReadOnlyMemory<byte>))
{
return converterType.Value;
return new ReadOnlyMemoryConverter();
}

if (type == typeof(DateTime))
if (type == typeof(byte[]))
{
return new DateTimeConverter(options);
return new ByteArrayConverter();
}

if (type.IsEnum)
Expand Down

0 comments on commit 26190f7

Please sign in to comment.