Skip to content

Commit 14db3bd

Browse files
author
Michael Catanzariti
committed
fix Bug : non first position discriminator is not supported #29
1 parent da22b56 commit 14db3bd

File tree

5 files changed

+177
-63
lines changed

5 files changed

+177
-63
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Dahomey.Cbor.Attributes;
2+
using Xunit;
3+
4+
namespace Dahomey.Cbor.Tests.Issues
5+
{
6+
public class Issue0029
7+
{
8+
public abstract class DummyParticle
9+
{
10+
11+
}
12+
13+
[CborDiscriminator("radix.particles.message", Policy = CborDiscriminatorPolicy.Always)]
14+
public class DummyMessageParticle : DummyParticle
15+
{
16+
public long nonce { get; protected set; }
17+
}
18+
19+
[Fact]
20+
public void Test()
21+
{
22+
CborOptions options = new CborOptions();
23+
options.Registry.DefaultDiscriminatorConvention.RegisterAssembly(typeof(DummyParticle).Assembly);
24+
25+
// {"nonce": 2181035975144481159, "_t": "radix.particles.message"}
26+
const string hexBuffer = "A2656E6F6E63651B1E4498A9ECD5A187625F747772616469782E7061727469636C65732E6D657373616765";
27+
DummyParticle obj = Helper.Read<DummyParticle>(hexBuffer, options);
28+
}
29+
}
30+
}

src/Dahomey.Cbor/Serialization/CborReader.cs

Lines changed: 91 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -34,35 +34,45 @@ public interface ICborArrayReader<TC>
3434
void ReadArrayItem(ref CborReader reader, ref TC context);
3535
}
3636

37-
public ref struct CborReader
37+
public enum CborReaderState
3838
{
39-
private enum State
40-
{
41-
Start,
42-
Header,
43-
Data
44-
}
39+
Start,
40+
Header,
41+
Data
42+
}
4543

46-
[StructLayout(LayoutKind.Explicit, Size = 2)]
47-
private struct Header
48-
{
49-
[FieldOffset(0)]
50-
public CborMajorType MajorType;
44+
[StructLayout(LayoutKind.Explicit, Size = 2)]
45+
public struct CborReaderHeader
46+
{
47+
[FieldOffset(0)]
48+
public CborMajorType MajorType;
5149

52-
[FieldOffset(1)]
53-
public byte AdditionalValue;
50+
[FieldOffset(1)]
51+
public byte AdditionalValue;
5452

55-
[FieldOffset(1)]
56-
public CborPrimitive Primitive;
57-
}
53+
[FieldOffset(1)]
54+
public CborPrimitive Primitive;
55+
}
5856

57+
public ref struct CborReaderBookmark
58+
{
59+
public ReadOnlySpan<byte> buffer;
60+
public int currentPos;
61+
public CborReaderState state;
62+
public CborReaderHeader header;
63+
public int remainingItemCount;
64+
}
65+
66+
public ref struct CborReader
67+
{
5968
private const int CHUNK_SIZE = 1024;
6069
private const byte INDEFINITE_LENGTH = 31;
6170

6271
private ReadOnlySpan<byte> _buffer;
6372
private int _currentPos;
64-
private State _state;
65-
private Header _header;
73+
private CborReaderState _state;
74+
private CborReaderHeader _header;
75+
private int _remainingItemCount;
6676

6777
public CborOptions Options { get; }
6878

@@ -71,8 +81,9 @@ public CborReader(ReadOnlySpan<byte> buffer, CborOptions options = null)
7181
_buffer = buffer;
7282
Options = options ?? CborOptions.Default;
7383
_currentPos = 0;
74-
_state = State.Start;
75-
_header = new Header();
84+
_state = CborReaderState.Start;
85+
_header = new CborReaderHeader();
86+
_remainingItemCount = 0;
7687
}
7788

7889
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -81,7 +92,7 @@ public bool TryReadSemanticTag(out ulong semanticTag)
8192
if (Accept(CborMajorType.SemanticTag))
8293
{
8394
semanticTag = ReadInteger();
84-
_state = State.Data;
95+
_state = CborReaderState.Data;
8596
return true;
8697
}
8798

@@ -93,7 +104,7 @@ public bool TryReadSemanticTag(out ulong semanticTag)
93104
public CborDataItemType GetCurrentDataItemType()
94105
{
95106
SkipSemanticTag();
96-
Header header = GetHeader();
107+
CborReaderHeader header = GetHeader();
97108

98109
switch (header.MajorType)
99110
{
@@ -149,6 +160,28 @@ public CborDataItemType GetCurrentDataItemType()
149160
}
150161
}
151162

163+
public CborReaderBookmark GetBookmark()
164+
{
165+
CborReaderBookmark bookmark;
166+
167+
bookmark.buffer = _buffer;
168+
bookmark.currentPos = _currentPos;
169+
bookmark.state = _state;
170+
bookmark.header = _header;
171+
bookmark.remainingItemCount = _remainingItemCount;
172+
173+
return bookmark;
174+
}
175+
176+
public void ReturnToBookmark(CborReaderBookmark bookmark)
177+
{
178+
_buffer = bookmark.buffer;
179+
_currentPos = bookmark.currentPos;
180+
_state = bookmark.state;
181+
_header = bookmark.header;
182+
_remainingItemCount = bookmark.remainingItemCount;
183+
}
184+
152185
[MethodImpl(MethodImplOptions.AggressiveInlining)]
153186
public void ReadBeginArray()
154187
{
@@ -316,7 +349,7 @@ public int ReadSize()
316349
{
317350
if (GetHeader().AdditionalValue == INDEFINITE_LENGTH)
318351
{
319-
_state = State.Data;
352+
_state = CborReaderState.Data;
320353
return -1;
321354
}
322355

@@ -328,17 +361,29 @@ public void ReadMap<TC>(ICborMapReader<TC> mapReader, ref TC context)
328361
{
329362
ReadBeginMap();
330363

331-
int size = ReadSize();
364+
int previousRemainingItemCount = _remainingItemCount;
365+
_remainingItemCount = ReadSize();
332366

333-
mapReader.ReadBeginMap(size, ref context);
367+
mapReader.ReadBeginMap(_remainingItemCount, ref context);
334368

335-
while (size > 0 || size < 0 && GetCurrentDataItemType() != CborDataItemType.Break)
369+
while (MoveNextMapItem())
336370
{
337371
mapReader.ReadMapItem(ref this, ref context);
338-
size--;
339372
}
340373

341-
_state = State.Start;
374+
_state = CborReaderState.Start;
375+
_remainingItemCount = previousRemainingItemCount;
376+
}
377+
378+
public bool MoveNextMapItem()
379+
{
380+
if (_remainingItemCount == 0 || _remainingItemCount < 0 && GetCurrentDataItemType() == CborDataItemType.Break)
381+
{
382+
return false;
383+
}
384+
385+
_remainingItemCount--;
386+
return true;
342387
}
343388

344389
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -356,13 +401,13 @@ public void ReadArray<TC>(ICborArrayReader<TC> arrayReader, ref TC context)
356401
size--;
357402
}
358403

359-
_state = State.Start;
404+
_state = CborReaderState.Start;
360405
}
361406

362407
[MethodImpl(MethodImplOptions.AggressiveInlining)]
363408
private ulong ReadUnsigned(ulong maxValue)
364409
{
365-
Header header = GetHeader();
410+
CborReaderHeader header = GetHeader();
366411

367412
switch (header.MajorType)
368413
{
@@ -377,7 +422,7 @@ private ulong ReadUnsigned(ulong maxValue)
377422
[MethodImpl(MethodImplOptions.AggressiveInlining)]
378423
private long ReadSigned(long maxValue)
379424
{
380-
Header header = GetHeader();
425+
CborReaderHeader header = GetHeader();
381426

382427
switch (header.MajorType)
383428
{
@@ -395,7 +440,7 @@ private long ReadSigned(long maxValue)
395440
[MethodImpl(MethodImplOptions.AggressiveInlining)]
396441
private ulong ReadInteger(ulong maxValue = ulong.MaxValue)
397442
{
398-
Header header = GetHeader();
443+
CborReaderHeader header = GetHeader();
399444

400445
ulong value;
401446

@@ -429,7 +474,7 @@ private ulong ReadInteger(ulong maxValue = ulong.MaxValue)
429474

430475
default:
431476
value = header.AdditionalValue;
432-
_state = State.Data;
477+
_state = CborReaderState.Data;
433478
break;
434479
}
435480

@@ -517,9 +562,9 @@ private ReadOnlySpan<byte> ReadSizeAndBytes()
517562
}
518563

519564
[MethodImpl(MethodImplOptions.AggressiveInlining)]
520-
private Header GetHeader()
565+
private CborReaderHeader GetHeader()
521566
{
522-
if (_state == State.Header)
567+
if (_state == CborReaderState.Header)
523568
{
524569
return _header;
525570
}
@@ -535,7 +580,7 @@ private Header GetHeader()
535580
}
536581

537582
Advance();
538-
_state = State.Header;
583+
_state = CborReaderState.Header;
539584

540585
return _header;
541586
}
@@ -570,15 +615,15 @@ private void SkipSemanticTag()
570615
if (Accept(CborMajorType.SemanticTag))
571616
{
572617
ReadInteger();
573-
_state = State.Data;
618+
_state = CborReaderState.Data;
574619
return;
575620
}
576621
}
577622

578623
[MethodImpl(MethodImplOptions.AggressiveInlining)]
579624
public void SkipDataItem()
580625
{
581-
Header header = GetHeader();
626+
CborReaderHeader header = GetHeader();
582627

583628
switch (header.MajorType)
584629
{
@@ -601,7 +646,7 @@ public void SkipDataItem()
601646
break;
602647

603648
case CborMajorType.SemanticTag:
604-
_state = State.Data;
649+
_state = CborReaderState.Data;
605650
break;
606651

607652
case CborMajorType.Primitive:
@@ -613,7 +658,7 @@ public void SkipDataItem()
613658
case CborPrimitive.Undefined:
614659
case CborPrimitive.SimpleValue:
615660
case CborPrimitive.Break:
616-
_state = State.Data;
661+
_state = CborReaderState.Data;
617662
break;
618663

619664
case CborPrimitive.HalfFloat:
@@ -643,7 +688,7 @@ private void SkipArray()
643688
size--;
644689
}
645690

646-
_state = State.Start;
691+
_state = CborReaderState.Start;
647692
}
648693

649694
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -658,15 +703,15 @@ private void SkipMap()
658703
size--;
659704
}
660705

661-
_state = State.Start;
706+
_state = CborReaderState.Start;
662707
}
663708

664709
[MethodImpl(MethodImplOptions.AggressiveInlining)]
665710
private bool Accept(CborPrimitive primitive)
666711
{
667712
if (Accept(CborMajorType.Primitive) && _header.Primitive == primitive)
668713
{
669-
_state = State.Data;
714+
_state = CborReaderState.Data;
670715
return true;
671716
}
672717

@@ -709,9 +754,9 @@ private void ExpectLength(int length)
709754
[MethodImpl(MethodImplOptions.AggressiveInlining)]
710755
private void Advance(int length = 1)
711756
{
712-
if (_state == State.Header)
757+
if (_state == CborReaderState.Header)
713758
{
714-
_state = State.Data;
759+
_state = CborReaderState.Data;
715760
}
716761
_buffer = _buffer.Slice(length);
717762
_currentPos += length;

src/Dahomey.Cbor/Serialization/Conventions/DefaultDiscriminatorConvention.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class DefaultDiscriminatorConvention : IDiscriminatorConvention
1313
private readonly ReadOnlyMemory<byte> _memberName = Encoding.ASCII.GetBytes("_t");
1414
private readonly ByteBufferDictionary<Type> _typesByDiscriminator = new ByteBufferDictionary<Type>();
1515
private readonly Dictionary<Type, ReadOnlyMemory<byte>> _discriminatorsByType = new Dictionary<Type, ReadOnlyMemory<byte>>();
16+
private readonly HashSet<Type> _discriminatedTypes = new HashSet<Type>();
1617

1718
public ReadOnlySpan<byte> MemberName => _memberName.Span;
1819

@@ -38,6 +39,16 @@ public void WriteDiscriminator<T>(ref CborWriter writer, Type actualType)
3839
writer.WriteString(discriminator.Span);
3940
}
4041

42+
/// <summary>
43+
/// Returns whether the given type has any discriminators registered for any of its subclasses.
44+
/// </summary>
45+
/// <param name="type">A Type.</param>
46+
/// <returns>True if the type is discriminated.</returns>
47+
public bool IsTypeDiscriminated(Type type)
48+
{
49+
return type.IsInterface || _discriminatedTypes.Contains(type);
50+
}
51+
4152
public void RegisterAssembly(Assembly assembly)
4253
{
4354
foreach (Type type in assembly.GetTypes()
@@ -52,6 +63,12 @@ public void RegisterType(Type type, ReadOnlyMemory<byte> discriminator)
5263
{
5364
_typesByDiscriminator.Add(discriminator.Span, type);
5465
_discriminatorsByType[type] = discriminator;
66+
67+
// mark all base types as discriminated (so we know that it's worth reading a discriminator)
68+
for (Type baseType = type.BaseType; baseType != null; baseType = baseType.BaseType)
69+
{
70+
_discriminatedTypes.Add(baseType);
71+
}
5572
}
5673
}
5774
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Dahomey.Cbor.Serialization.Converters;
2-
using System;
1+
using System;
32

43
namespace Dahomey.Cbor.Serialization.Conventions
54
{
@@ -8,5 +7,6 @@ public interface IDiscriminatorConvention
87
ReadOnlySpan<byte> MemberName { get; }
98
Type ReadDiscriminator(ref CborReader reader);
109
void WriteDiscriminator<T>(ref CborWriter writer, Type actualType) where T : class;
10+
bool IsTypeDiscriminated(Type type);
1111
}
1212
}

0 commit comments

Comments
 (0)