Skip to content

Commit 0251db2

Browse files
committed
Initial CBOR work
1 parent 4c916a9 commit 0251db2

File tree

5 files changed

+248
-149
lines changed

5 files changed

+248
-149
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Buffers;
2+
3+
namespace Serde.Cbor;
4+
5+
partial class CborReader<TReader>
6+
{
7+
private sealed class EnumDeserializer(CborReader<TReader> reader) : ITypeDeserializer
8+
{
9+
public int? SizeOpt => null;
10+
public bool ReadBool(ISerdeInfo info, int index) => reader.ReadBool();
11+
public void ReadBytes(ISerdeInfo info, int index, IBufferWriter<byte> writer) => reader.ReadBytes(writer);
12+
public char ReadChar(ISerdeInfo info, int index) => reader.ReadChar();
13+
public DateTime ReadDateTime(ISerdeInfo info, int index) => reader.ReadDateTime();
14+
public decimal ReadDecimal(ISerdeInfo info, int index) => reader.ReadDecimal();
15+
public float ReadF32(ISerdeInfo info, int index) => reader.ReadF32();
16+
public double ReadF64(ISerdeInfo info, int index) => reader.ReadF64();
17+
public short ReadI16(ISerdeInfo info, int index) => reader.ReadI16();
18+
public int ReadI32(ISerdeInfo info, int index) => reader.ReadI32();
19+
public long ReadI64(ISerdeInfo info, int index) => reader.ReadI64();
20+
public sbyte ReadI8(ISerdeInfo info, int index) => reader.ReadI8();
21+
public string ReadString(ISerdeInfo info, int index) => reader.ReadString();
22+
public ushort ReadU16(ISerdeInfo info, int index) => reader.ReadU16();
23+
public uint ReadU32(ISerdeInfo info, int index) => reader.ReadU32();
24+
public ulong ReadU64(ISerdeInfo info, int index) => reader.ReadU64();
25+
26+
public byte ReadU8(ISerdeInfo info, int index) => reader.ReadU8();
27+
28+
public T ReadValue<T>(ISerdeInfo info, int index, IDeserialize<T> deserialize) where T : class?
29+
=> deserialize.Deserialize(reader);
30+
31+
public void SkipValue(ISerdeInfo info, int index)
32+
{
33+
throw new NotImplementedException();
34+
}
35+
36+
public int TryReadIndex(ISerdeInfo info, out string? errorName)
37+
{
38+
// Enums are serialized as the index of the enum member
39+
errorName = null;
40+
return reader.ReadI32();
41+
}
42+
}
43+
}

src/reader/CborReader.ITypeDeserializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ namespace Serde.Cbor;
55

66
partial class CborReader<TReader>
77
{
8-
private struct DeserializeType(CborReader<TReader> deserializer) : ITypeDeserializer
8+
private struct DeserializeType(CborReader<TReader> deserializer, int? mapLength) : ITypeDeserializer
99
{
10-
int? ITypeDeserializer.SizeOpt => null;
10+
int? ITypeDeserializer.SizeOpt => mapLength;
1111

1212
private int _count;
1313

src/reader/CborReader.cs

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,15 @@ private ITypeDeserializer ReadCollection(ISerdeInfo typeInfo)
143143
else if (typeInfo.Kind == InfoKind.Dictionary)
144144
{
145145
int length;
146-
if (b <= 0x8f)
146+
if (b is >= 0xa0 and <= 0xb7)
147147
{
148-
length = b & 0xf;
148+
length = b - 0xa0;
149+
}
150+
else if (b == 0xb8)
151+
{
152+
length = EatByteOrThrow();
149153
}
150-
else if (b == 0xde)
154+
else if (b == 0xb9)
151155
{
152156
length = ReadBigEndianU16();
153157
}
@@ -180,7 +184,7 @@ private double ReadF64()
180184
span = RefillNoEof(0);
181185
}
182186
var b = span[0];
183-
if (b != 0xcb)
187+
if (b != 0xfb)
184188
{
185189
throw new Exception($"Expected 64-bit double, got 0x{b:x}");
186190
}
@@ -199,7 +203,7 @@ private float ReadF32()
199203
span = RefillNoEof(5);
200204
}
201205
var b = span[0];
202-
if (b != 0xca)
206+
if (b != 0xfa)
203207
{
204208
throw new Exception($"Expected 32-bit float, got 0x{b:x}");
205209
}
@@ -388,9 +392,10 @@ public void ReadBytes(IBufferWriter<byte> writer)
388392
var b = EatByteOrThrow();
389393
int length = b switch
390394
{
391-
0xc4 => EatByteOrThrow(),
392-
0xc5 => ReadBigEndianU16(), // 16-bit length
393-
0xc6 => checked((int)ReadBigEndianU32()), // 32-bit length
395+
>= 0x40 and <= 0x57 => b - 0x40,
396+
0x58 => EatByteOrThrow(),
397+
0x59 => ReadBigEndianU16(), // 16-bit length
398+
0x5a => checked((int)ReadBigEndianU32()), // 32-bit length
394399
_ => throw new DeserializeException($"Expected bytes, got 0x{b:x}"),
395400
};
396401
if (!_reader.FillBuffer(length))
@@ -446,50 +451,47 @@ private ReadOnlySpan<byte> ReadUtf8Span()
446451

447452
ITypeDeserializer IDeserializer.ReadType(ISerdeInfo typeInfo)
448453
{
449-
if (typeInfo.Kind == InfoKind.List || typeInfo.Kind == InfoKind.Dictionary)
454+
switch (typeInfo.Kind)
455+
{
456+
case InfoKind.List:
457+
case InfoKind.Dictionary:
458+
return ReadCollection(typeInfo);
459+
case InfoKind.Enum:
460+
return new EnumDeserializer(this);
461+
case InfoKind.CustomType:
462+
// Custom types are serialized as a map
463+
int? length = ReadMapLength();
464+
return new DeserializeType(this, length);
465+
default:
466+
throw new ArgumentException("Unexpected info kind: " + typeInfo.Kind);
467+
}
468+
}
469+
470+
private int ReadMapLength()
471+
{
472+
var b = EatByteOrThrow();
473+
int length;
474+
if (b is >= 0xa0 and <= 0xb7)
450475
{
451-
return ReadCollection(typeInfo);
476+
length = b - 0xa0;
452477
}
453-
else if (typeInfo.Kind == InfoKind.CustomType)
478+
else if (b == 0xb8)
454479
{
455-
var fieldCount = typeInfo.FieldCount;
456-
var b = EatByteOrThrow();
457-
int length;
458-
if (fieldCount <= 0xff)
459-
{
460-
if (b > 0x9f)
461-
{
462-
throw new Exception($"Expected array, got 0x{b:x}");
463-
}
464-
length = b & 0xf;
465-
}
466-
else if (fieldCount <= 0xffff)
467-
{
468-
if (b != 0xdc)
469-
{
470-
throw new Exception($"Expected 16-bit array, got 0x{b:x}");
471-
}
472-
length = ReadBigEndianU16();
473-
}
474-
else
475-
{
476-
if (b != 0xdd)
477-
{
478-
throw new Exception($"Expected 32-bit array, got 0x{b:x}");
479-
}
480-
length = (int)ReadBigEndianU32();
481-
}
482-
if (length != fieldCount)
483-
{
484-
throw new Exception($"Expected array of length {fieldCount}, got {length}");
485-
}
486-
return new DeserializeType(this);
480+
length = EatByteOrThrow();
481+
}
482+
else if (b == 0xb9)
483+
{
484+
length = ReadBigEndianU16();
487485
}
488-
else if (typeInfo.Kind == InfoKind.Enum)
486+
else if (b == 0xba)
487+
{
488+
length = (int)ReadBigEndianU32();
489+
}
490+
else
489491
{
490-
return new DeserializeType(this);
492+
throw new DeserializeException($"Expected map, got 0x{b:x}");
491493
}
492-
throw new Exception("Expected custom type or enum");
494+
return length;
493495
}
494496

495497
private ushort ReadU16()

src/writer/CborWriter.cs

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,23 @@ ITypeSerializer ISerializer.WriteCollection(ISerdeInfo typeInfo, int? length)
3434
}
3535
if (typeInfo.Kind == InfoKind.List)
3636
{
37-
if (length <= 15)
37+
if (length <= 0x17)
3838
{
39-
_out.Add((byte)(0x90 | length));
39+
_out.Add((byte)(0x80 + length));
4040
}
41-
else if (length <= 0xffff)
41+
else if (length <= byte.MaxValue)
4242
{
43-
_out.Add(0xdc);
43+
_out.Add(0x98);
44+
_out.Add((byte)length);
45+
}
46+
else if (length <= ushort.MaxValue)
47+
{
48+
_out.Add(0x99);
4449
WriteBigEndian((ushort)length);
4550
}
4651
else
4752
{
48-
_out.Add(0xdd);
53+
_out.Add(0x9a);
4954
WriteBigEndian((uint)length);
5055
}
5156
}
@@ -62,18 +67,23 @@ ITypeSerializer ISerializer.WriteCollection(ISerdeInfo typeInfo, int? length)
6267

6368
private void WriteMapLength(int length)
6469
{
65-
if (length <= 15)
70+
if (length <= 0x17)
71+
{
72+
_out.Add((byte)(0xa0 + length));
73+
}
74+
else if (length <= byte.MaxValue)
6675
{
67-
_out.Add((byte)(0x80 | length));
76+
_out.Add(0xb8);
77+
_out.Add((byte)length);
6878
}
69-
else if (length <= 0xffff)
79+
else if (length <= ushort.MaxValue)
7080
{
71-
_out.Add(0xde);
81+
_out.Add(0xb9);
7282
WriteBigEndian((ushort)length);
7383
}
7484
else
7585
{
76-
_out.Add(0xdf);
86+
_out.Add(0xba);
7787
WriteBigEndian((uint)length);
7888
}
7989
}
@@ -85,13 +95,13 @@ public void WriteDecimal(decimal d)
8595

8696
public void WriteF64(double d)
8797
{
88-
_out.Add(0xcb);
98+
_out.Add(0xfb);
8999
WriteBigEndian(d);
90100
}
91101

92102
public void WriteF32(float f)
93103
{
94-
_out.Add(0xca);
104+
_out.Add(0xfa);
95105
WriteBigEndian(f);
96106
}
97107

@@ -134,7 +144,7 @@ private void WriteI64(long i64)
134144
}
135145
public void WriteNull()
136146
{
137-
_out.Add(0xc0);
147+
_out.Add(0xf6);
138148
}
139149

140150
public void WriteDateTimeOffset(DateTimeOffset dt)
@@ -149,31 +159,27 @@ public void WriteDateTime(DateTime dt)
149159

150160
public void WriteBytes(ReadOnlyMemory<byte> bytes)
151161
{
152-
byte code = bytes.Length switch
162+
var bytesLen = bytes.Length;
163+
(byte code, int prefixLen) = bytesLen switch
153164
{
154-
<= 0xff => 0xc4,
155-
<= 0xffff => 0xc5,
156-
_ => 0xc6
157-
};
158-
var prefixLen = code switch
159-
{
160-
0xc4 => 2,
161-
0xc5 => 3,
162-
_ => 5
165+
<= 0x17 => ((byte)(0x40 + bytesLen), 1),
166+
<= byte.MaxValue => ((byte)0x58, 2),
167+
<= ushort.MaxValue => ((byte)0x59, 3),
168+
_ => ((byte)0x5a, 5)
163169
};
164-
var span = _out.GetAppendSpan(prefixLen + bytes.Length);
165-
_out.Count += prefixLen + bytes.Length;
170+
var span = _out.GetAppendSpan(prefixLen + bytesLen);
171+
_out.Count += prefixLen + bytesLen;
166172
span[0] = code;
167173
switch (prefixLen)
168174
{
169175
case 2:
170-
span[1] = (byte)bytes.Length;
176+
span[1] = (byte)bytesLen;
171177
break;
172178
case 3:
173-
WriteBigEndian((ushort)bytes.Length);
179+
WriteBigEndian((ushort)bytesLen);
174180
break;
175181
case 5:
176-
WriteBigEndian((uint)bytes.Length);
182+
WriteBigEndian((uint)bytesLen);
177183
break;
178184
}
179185
bytes.Span.CopyTo(span[prefixLen..]);

0 commit comments

Comments
 (0)