Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CBOR] Implement tag and special value support for CborWriter and CborReader #34046

Merged
merged 8 commits into from
Mar 30, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public partial class CborReaderTests
[InlineData(new object[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, "98190102030405060708090a0b0c0d0e0f101112131415161718181819")]
[InlineData(new object[] { 1, -1, "", new byte[] { 7 } }, "840120604107")]
[InlineData(new object[] { "lorem", "ipsum", "dolor" }, "83656c6f72656d65697073756d65646f6c6f72")]
[InlineData(new object?[] { false, null, float.NaN, double.PositiveInfinity }, "84f4f6faffc00000fb7ff0000000000000")]
public static void ReadArray_SimpleValues_HappyPath(object[] expectedValues, string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
Expand Down Expand Up @@ -48,6 +49,7 @@ public static void ReadArray_NestedValues_HappyPath(object[] expectedValues, str
[InlineData(new object[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }, "9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff")]
[InlineData(new object[] { 1, -1, "", new byte[] { 7 } }, "9f0120604107ff")]
[InlineData(new object[] { "lorem", "ipsum", "dolor" }, "9f656c6f72656d65697073756d65646f6c6f72ff")]
[InlineData(new object?[] { false, null, float.NaN, double.PositiveInfinity }, "9ff4f6faffc00000fb7ff0000000000000ff")]
public static void ReadArray_IndefiniteLength_HappyPath(object[] expectedValues, string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ public static void VerifyValue(CborReader reader, object expectedValue, bool exp
{
switch (expectedValue)
{
case null:
Assert.Equal(CborReaderState.Null, reader.Peek());
reader.ReadNull();
break;
case bool expected:
Assert.Equal(CborReaderState.Boolean, reader.Peek());
bool b = reader.ReadBoolean();
Assert.Equal(expected, b);
break;
case int expected:
VerifyPeekInteger(reader, isUnsignedInteger: expected >= 0);
long i = reader.ReadInt64();
Expand All @@ -32,15 +41,25 @@ public static void VerifyValue(CborReader reader, object expectedValue, bool exp
ulong u = reader.ReadUInt64();
Assert.Equal(expected, u);
break;
case float expected:
Assert.Equal(CborReaderState.SinglePrecisionFloat, reader.Peek());
float f = reader.ReadSingle();
Assert.Equal(expected, f);
break;
case double expected:
Assert.Equal(CborReaderState.DoublePrecisionFloat, reader.Peek());
double d = reader.ReadDouble();
Assert.Equal(expected, d);
break;
case string expected:
Assert.Equal(CborReaderState.TextString, reader.Peek());
string s = reader.ReadTextString();
Assert.Equal(expected, s);
break;
case byte[] expected:
Assert.Equal(CborReaderState.ByteString, reader.Peek());
byte[] b = reader.ReadByteString();
Assert.Equal(expected.ByteArrayToHex(), b.ByteArrayToHex());
byte[] bytes = reader.ReadByteString();
Assert.Equal(expected.ByteArrayToHex(), bytes.ByteArrayToHex());
break;
case string[] expectedChunks:
Assert.Equal(CborReaderState.StartTextString, reader.Peek());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,49 @@ public static void ReadCborNegativeIntegerEncoding_SingleValue_HappyPath(ulong e
Assert.Equal(CborReaderState.Finished, reader.Peek());
}

[Theory]
[InlineData(2, 2, "c202")]
[InlineData(0, "2013-03-21T20:04:00Z", "c074323031332d30332d32315432303a30343a30305a")]
[InlineData(1, 1363896240, "c11a514b67b0")]
[InlineData(23, new byte[] { 1, 2, 3, 4 }, "d74401020304")]
[InlineData(32, "http://www.example.com", "d82076687474703a2f2f7777772e6578616d706c652e636f6d")]
[InlineData(int.MaxValue, 2, "da7fffffff02")]
[InlineData(ulong.MaxValue, new object[] { 1, 2 }, "dbffffffffffffffff820102")]
public static void ReadTag_SingleValue_HappyPath(ulong expectedTag, object expectedValue, string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding);

Assert.Equal(CborReaderState.Tag, reader.Peek());
CborTag tag = reader.ReadTag();
Assert.Equal(expectedTag, (ulong)tag);

Helpers.VerifyValue(reader, expectedValue);
Assert.Equal(CborReaderState.Finished, reader.Peek());
}

[Theory]
[InlineData(new ulong[] { 1, 2, 3 }, 2, "c1c2c302")]
[InlineData(new ulong[] { 0, 0, 0 }, "2013-03-21T20:04:00Z", "c0c0c074323031332d30332d32315432303a30343a30305a")]
[InlineData(new ulong[] { int.MaxValue, ulong.MaxValue }, 1363896240, "da7fffffffdbffffffffffffffff1a514b67b0")]
[InlineData(new ulong[] { 23, 24, 100 }, new byte[] { 1, 2, 3, 4 }, "d7d818d8644401020304")]
[InlineData(new ulong[] { 32, 1, 1 }, new object[] { 1, "lorem ipsum" }, "d820c1c182016b6c6f72656d20697073756d")]
public static void ReadTag_NestedTags_HappyPath(ulong[] expectedTags, object expectedValue, string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding);

foreach (ulong expectedTag in expectedTags)
{
Assert.Equal(CborReaderState.Tag, reader.Peek());
CborTag tag = reader.ReadTag();
Assert.Equal(expectedTag, (ulong)tag);
}

Helpers.VerifyValue(reader, expectedValue);
Assert.Equal(CborReaderState.Finished, reader.Peek());
}

[Theory]
// all possible definite-length encodings for the value 23
[InlineData("17")]
Expand Down Expand Up @@ -156,6 +199,16 @@ public static void ReadUInt64_OutOfRangeValues_ShouldThrowOverflowException(stri
Assert.Throws<OverflowException>(() => reader.ReadUInt64());
}

[Theory]
[InlineData("c2")]
public static void ReadTag_NoSubsequentData_ShouldPeekEndOfData(string hexEncoding)
{
byte[] data = hexEncoding.HexToByteArray();
var reader = new CborReader(data);
reader.ReadTag();
Assert.Equal(CborReaderState.EndOfData, reader.Peek());
}

[Theory]
[InlineData("40")] // empty text string
[InlineData("60")] // empty byte string
Expand All @@ -173,6 +226,67 @@ public static void ReadInt64_InvalidTypes_ShouldThrowInvalidOperationException(s
Assert.Equal("Data item major type mismatch.", exn.Message);
}

[Theory]
[InlineData("40")] // empty text string
[InlineData("60")] // empty byte string
[InlineData("f6")] // null
[InlineData("80")] // []
[InlineData("a0")] // {}
[InlineData("f97e00")] // NaN
[InlineData("fb3ff199999999999a")] // 1.1
public static void ReadTag_InvalidTypes_ShouldThrowInvalidOperationException(string hexEncoding)
{
byte[] data = hexEncoding.HexToByteArray();
var reader = new CborReader(data);
InvalidOperationException exn = Assert.Throws<InvalidOperationException>(() => reader.ReadTag());

Assert.Equal("Data item major type mismatch.", exn.Message);
}

[Fact]
public static void ReadTag_NestedTagWithMissingPayload_ShouldThrowFormatException()
{
byte[] data = "9fc2ff".HexToByteArray();
var reader = new CborReader(data);

reader.ReadStartArray();
reader.ReadTag();
Assert.Equal(CborReaderState.FormatError, reader.Peek());
Assert.Throws<FormatException>(() => reader.ReadEndArray());
}

[Theory]
[InlineData("8201c202")] // definite length array
[InlineData("9f01c202ff")] // equivalent indefinite-length array
public static void ReadTag_CallingEndReadArrayPrematurely_ShouldThrowInvalidOperationException(string hexEncoding)
{
// encoding is valid CBOR, so should not throw FormatException
byte[] data = hexEncoding.HexToByteArray();
var reader = new CborReader(data);

reader.ReadStartArray();
reader.ReadInt64();
reader.ReadTag();
Assert.Equal(CborReaderState.UnsignedInteger, reader.Peek());
Assert.Throws<InvalidOperationException>(() => reader.ReadEndArray());
}

[Theory]
[InlineData("a102c202")] // definite length map
[InlineData("bf02c202ff")] // equivalent indefinite-length map
public static void ReadTag_CallingEndReadMapPrematurely_ShouldThrowInvalidOperationException(string hexEncoding)
{
// encoding is valid CBOR, so should not throw FormatException
byte[] data = hexEncoding.HexToByteArray();
var reader = new CborReader(data);

reader.ReadStartMap();
reader.ReadInt64();
reader.ReadTag();
Assert.Equal(CborReaderState.UnsignedInteger, reader.Peek());
Assert.Throws<InvalidOperationException>(() => reader.ReadEndArray());
}

[Theory]
[InlineData("40")] // empty byte string
[InlineData("60")] // empty text string
Expand Down
Loading