Skip to content

Commit 5c264ef

Browse files
authored
Added decimal type support
1 parent e536b6e commit 5c264ef

File tree

7 files changed

+155
-1
lines changed

7 files changed

+155
-1
lines changed

src/Dahomey.Cbor.Tests/CborReaderTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,25 @@ public void ReadDouble(string hexBuffer, double expectedValue, Type expectedExce
212212
Helper.TestRead(nameof(CborReader.ReadDouble), hexBuffer, expectedValue, expectedExceptionType);
213213
}
214214

215+
[Theory]
216+
[InlineData("FC00013D2B9C2D125A0000000000080000", 3487324.89798234, null)]
217+
public void ReadDecimal(string hexBuffer, decimal expectedValue, Type expectedExceptionType)
218+
{
219+
Helper.TestRead(nameof(CborReader.ReadDecimal), hexBuffer, expectedValue, expectedExceptionType);
220+
}
221+
222+
[Theory]
223+
[InlineData("FC0DED017037E8D1950000000000100000", "100.3459873459458453", null)]
224+
[InlineData("FCFFFFFFFFFFFFFFFFFFFFFFFF80000000", "-79228162514264337593543950335", null)]
225+
[InlineData("FCFFFFFFFFFFFFFFFFFFFFFFFF00000000", "79228162514264337593543950335", null)]
226+
public void ReadDecimal2(string hexBuffer, string expectedValue, Type expectedExceptionType)
227+
{
228+
if (!decimal.TryParse(expectedValue, out decimal decimalValue)) throw new InvalidCastException("Specified string cannot be cast to a decimal value");
229+
230+
Helper.TestRead(nameof(CborReader.ReadDecimal), hexBuffer, decimalValue, expectedExceptionType);
231+
}
232+
233+
215234
[Theory]
216235
[InlineData("63666F6F", "foo", null)]
217236
[InlineData("7F6166616F616FFF", "foo", typeof(NotSupportedException))]

src/Dahomey.Cbor.Tests/CborWriterTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,24 @@ public void WriteDouble(string hexBuffer, double value, Type expectedExceptionTy
208208
Helper.TestWrite(nameof(CborWriter.WriteDouble), value, hexBuffer, expectedExceptionType);
209209
}
210210

211+
[Theory]
212+
[InlineData("FC00013D2B9C2D125A0000000000080000", 3487324.89798234, null)]
213+
public void WriteDecimal(string hexBuffer, decimal value, Type expectedExceptionType)
214+
{
215+
Helper.TestWrite(nameof(CborWriter.WriteDecimal), value, hexBuffer, expectedExceptionType);
216+
}
217+
218+
[Theory]
219+
[InlineData("FC0DED017037E8D1950000000000100000", "100.3459873459458453", null)]
220+
[InlineData("FCFFFFFFFFFFFFFFFFFFFFFFFF80000000", "-79228162514264337593543950335", null)]
221+
[InlineData("FCFFFFFFFFFFFFFFFFFFFFFFFF00000000", "79228162514264337593543950335", null)]
222+
public void WriteDecimal2(string hexBuffer, string value, Type expectedExceptionType)
223+
{
224+
if (!decimal.TryParse(value, out decimal decimalValue)) throw new InvalidCastException("Specified string cannot be cast to a decimal value");
225+
226+
Helper.TestWrite(nameof(CborWriter.WriteDecimal), decimalValue, hexBuffer, expectedExceptionType);
227+
}
228+
211229
[Theory]
212230
[InlineData("63666F6F", "foo", null)]
213231
[InlineData("F6", null, null)]

src/Dahomey.Cbor/Serialization/CborPrimitive.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public enum CborPrimitive
1010
HalfFloat = 25,
1111
SingleFloat = 26,
1212
DoubleFloat = 27,
13+
DecimalFloat = 28,
1314
Break = 31
1415
}
1516
}

src/Dahomey.Cbor/Serialization/CborReader.cs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ public enum CborDataItemType
1919
ByteString,
2020
Array,
2121
Map,
22-
Break
22+
Break,
23+
24+
Decimal
2325
}
2426

2527
public interface ICborMapReader<TC>
@@ -149,6 +151,9 @@ public CborDataItemType GetCurrentDataItemType()
149151
case CborPrimitive.DoubleFloat:
150152
return CborDataItemType.Double;
151153

154+
case CborPrimitive.DecimalFloat:
155+
return CborDataItemType.Decimal;
156+
152157
case CborPrimitive.Break:
153158
return CborDataItemType.Break;
154159

@@ -416,6 +421,46 @@ public double ReadDouble()
416421
}
417422
}
418423

424+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
425+
public decimal ReadDecimal()
426+
{
427+
SkipSemanticTag();
428+
CborReaderHeader header = GetHeader();
429+
430+
switch (header.MajorType)
431+
{
432+
case CborMajorType.PositiveInteger:
433+
return ReadInteger();
434+
435+
case CborMajorType.NegativeInteger:
436+
return -1L - (long)ReadInteger();
437+
438+
case CborMajorType.Primitive:
439+
{
440+
switch (header.Primitive)
441+
{
442+
case CborPrimitive.HalfFloat:
443+
return (decimal)InternalReadHalf();
444+
445+
case CborPrimitive.SingleFloat:
446+
return Convert.ToDecimal(InternalReadSingle());
447+
448+
case CborPrimitive.DoubleFloat:
449+
return Convert.ToDecimal(InternalReadDouble());
450+
451+
case CborPrimitive.DecimalFloat:
452+
return InternalReadDecimal();
453+
454+
default:
455+
throw new CborException($"Invalid primitive {header.Primitive}");
456+
}
457+
}
458+
459+
default:
460+
throw new CborException($"Invalid major type {header.MajorType}");
461+
}
462+
}
463+
419464
[MethodImpl(MethodImplOptions.AggressiveInlining)]
420465
public int ReadSize()
421466
{
@@ -603,6 +648,26 @@ private double InternalReadDouble()
603648
}
604649
}
605650

651+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
652+
private decimal InternalReadDecimal()
653+
{
654+
ReadOnlySpan<byte> bytes = ReadBytes(16);
655+
656+
if (BitConverter.IsLittleEndian)
657+
{
658+
int i1 = BinaryPrimitives.ReadInt32BigEndian(bytes.Slice(0, 4));
659+
int i0 = BinaryPrimitives.ReadInt32BigEndian(bytes.Slice(4, 4));
660+
int i2 = BinaryPrimitives.ReadInt32BigEndian(bytes.Slice(8, 4));
661+
int i3 = BinaryPrimitives.ReadInt32BigEndian(bytes.Slice(12, 4));
662+
663+
return new decimal(new int[] { i0, i1, i2, i3 });
664+
}
665+
else
666+
{
667+
return MemoryMarshal.Read<decimal>(bytes);
668+
}
669+
}
670+
606671
[MethodImpl(MethodImplOptions.AggressiveInlining)]
607672
private ReadOnlySpan<byte> ReadSizeAndBytes()
608673
{
@@ -727,6 +792,10 @@ public void SkipDataItem()
727792
case CborPrimitive.DoubleFloat:
728793
Advance(8);
729794
break;
795+
796+
case CborPrimitive.DecimalFloat:
797+
Advance(16);
798+
break;
730799
}
731800
break;
732801
}

src/Dahomey.Cbor/Serialization/CborWriter.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ public void WriteDouble(double value)
171171
InternalWriteDouble(value);
172172
}
173173

174+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
175+
public void WriteDecimal(decimal value)
176+
{
177+
InternalWriteDecimal(value);
178+
}
179+
174180
[MethodImpl(MethodImplOptions.AggressiveInlining)]
175181
public void WriteString(string? value)
176182
{
@@ -385,6 +391,31 @@ private void InternalWriteDouble(double value)
385391
_bufferWriter.Advance(8);
386392
}
387393

394+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
395+
private void InternalWriteDecimal(decimal value)
396+
{
397+
WritePrimitive(CborPrimitive.DecimalFloat);
398+
399+
Span<byte> bytes = _bufferWriter.GetSpan(16);
400+
var span = _bufferWriter.GetSpan(16);
401+
402+
if (BitConverter.IsLittleEndian)
403+
{
404+
int[] bits = decimal.GetBits(value);
405+
406+
BinaryPrimitives.WriteInt32BigEndian(span.Slice(0, 4), bits[1]);
407+
BinaryPrimitives.WriteInt32BigEndian(span.Slice(4, 4), bits[0]);
408+
BinaryPrimitives.WriteInt32BigEndian(span.Slice(8, 4), bits[2]);
409+
BinaryPrimitives.WriteInt32BigEndian(span.Slice(12, 4), bits[3]);
410+
}
411+
else
412+
{
413+
MemoryMarshal.Write(span, ref value);
414+
}
415+
416+
_bufferWriter.Advance(16);
417+
}
418+
388419
[MethodImpl(MethodImplOptions.AggressiveInlining)]
389420
private void WritePrimitive(CborPrimitive primitive)
390421
{
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace Dahomey.Cbor.Serialization.Converters
2+
{
3+
public class DecimalConverter : CborConverterBase<decimal>
4+
{
5+
public override decimal Read(ref CborReader reader)
6+
{
7+
return reader.ReadDecimal();
8+
}
9+
10+
public override void Write(ref CborWriter writer, decimal value)
11+
{
12+
writer.WriteDecimal(value);
13+
}
14+
}
15+
}

src/Dahomey.Cbor/Serialization/Converters/Providers/PrimitiveConverterProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class PrimitiveConverterProvider : CborConverterProviderBase
1919
[typeof(ulong)] = typeof(UInt64Converter),
2020
[typeof(float)] = typeof(SingleConverter),
2121
[typeof(double)] = typeof(DoubleConverter),
22+
[typeof(decimal)] = typeof(DecimalConverter),
2223
[typeof(string)] = typeof(StringConverter),
2324
[typeof(DateTime)] = typeof(DateTimeConverter),
2425
[typeof(ReadOnlyMemory<byte>)] = typeof(ReadOnlyMemoryConverter),

0 commit comments

Comments
 (0)