This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new c85f3c06b9 IGNITE-18588 .NET: Fix BinaryTupleReader behavior on
payload size mismatch (#1554)
c85f3c06b9 is described below
commit c85f3c06b92e84bc8795c0862bb9d7872a296675
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Fri Jan 20 12:21:44 2023 +0200
IGNITE-18588 .NET: Fix BinaryTupleReader behavior on payload size mismatch
(#1554)
* Throw exception instead of returning incorrect value when field payload
size has unexpected length.
* Add tests for type compatibility - long can be read as int (if it fits),
etc.
---
.../Proto/BinaryTuple/BinaryTupleTests.cs | 74 ++++++++++++++++++++++
.../Proto/BinaryTuple/BinaryTupleReader.cs | 54 ++++++++++------
2 files changed, 109 insertions(+), 19 deletions(-)
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
index 3b2170c145..a93aef1c7f 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
@@ -124,6 +124,9 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
var reader = new BinaryTupleReader(res, 1);
Assert.AreEqual(value, reader.GetByte(0));
+ Assert.AreEqual(value, reader.GetShort(0));
+ Assert.AreEqual(value, reader.GetInt(0));
+ Assert.AreEqual(value, reader.GetLong(0));
}
[Test]
@@ -139,7 +142,10 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(value != 0 ? 3 : 2, bytes.Length);
var reader = new BinaryTupleReader(bytes, 1);
+ Assert.AreEqual(value, reader.GetByte(0));
Assert.AreEqual(value, reader.GetShort(0));
+ Assert.AreEqual(value, reader.GetInt(0));
+ Assert.AreEqual(value, reader.GetLong(0));
}
values = new short[] { short.MinValue, sbyte.MinValue - 1,
sbyte.MaxValue + 1, short.MaxValue };
@@ -153,6 +159,8 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
var reader = new BinaryTupleReader(bytes, 1);
Assert.AreEqual(value, reader.GetShort(0));
+ Assert.AreEqual(value, reader.GetInt(0));
+ Assert.AreEqual(value, reader.GetLong(0));
}
}
@@ -168,7 +176,10 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(value != 0 ? 3 : 2, bytes.Length);
var reader = new BinaryTupleReader(bytes, 1);
+ Assert.AreEqual(value, reader.GetByte(0));
+ Assert.AreEqual(value, reader.GetShort(0));
Assert.AreEqual(value, reader.GetInt(0));
+ Assert.AreEqual(value, reader.GetLong(0));
}
values = new[] { short.MinValue, sbyte.MinValue - 1,
sbyte.MaxValue + 1, short.MaxValue };
@@ -180,7 +191,9 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(4, bytes.Length);
var reader = new BinaryTupleReader(bytes, 1);
+ Assert.AreEqual(value, reader.GetShort(0));
Assert.AreEqual(value, reader.GetInt(0));
+ Assert.AreEqual(value, reader.GetLong(0));
}
values = new[] { int.MinValue, short.MinValue - 1, short.MaxValue
+ 1, int.MaxValue };
@@ -193,6 +206,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
BinaryTupleReader reader = new BinaryTupleReader(bytes, 1);
Assert.AreEqual(value, reader.GetInt(0));
+ Assert.AreEqual(value, reader.GetLong(0));
}
}
@@ -208,6 +222,9 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(value != 0 ? 3 : 2, bytes.Length);
BinaryTupleReader reader = new BinaryTupleReader(bytes, 1);
+ Assert.AreEqual(value, reader.GetByte(0));
+ Assert.AreEqual(value, reader.GetShort(0));
+ Assert.AreEqual(value, reader.GetInt(0));
Assert.AreEqual(value, reader.GetLong(0));
}
@@ -220,6 +237,8 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(4, bytes.Length);
BinaryTupleReader reader = new BinaryTupleReader(bytes, 1);
+ Assert.AreEqual(value, reader.GetShort(0));
+ Assert.AreEqual(value, reader.GetInt(0));
Assert.AreEqual(value, reader.GetLong(0));
}
@@ -232,6 +251,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(6, bytes.Length);
BinaryTupleReader reader = new BinaryTupleReader(bytes, 1);
+ Assert.AreEqual(value, reader.GetInt(0));
Assert.AreEqual(value, reader.GetLong(0));
}
@@ -863,6 +883,60 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(Instant.FromDateTimeUtc(utcNow),
reader.GetObject(48));
}
+ [Test]
+ public void TestInvalidElementLengthThrowsException()
+ {
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendBytes(new
byte[1000]));
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetByte(0), 1);
+ Test(() => new BinaryTupleReader(bytes, 1).GetByteNullable(0), 1);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetShort(0), 2);
+ Test(() => new BinaryTupleReader(bytes, 1).GetShortNullable(0), 2);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetInt(0), 4);
+ Test(() => new BinaryTupleReader(bytes, 1).GetIntNullable(0), 4);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetLong(0), 8);
+ Test(() => new BinaryTupleReader(bytes, 1).GetLongNullable(0), 8);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetFloat(0), 4);
+ Test(() => new BinaryTupleReader(bytes, 1).GetFloatNullable(0), 4);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetDouble(0), 8);
+ Test(() => new BinaryTupleReader(bytes, 1).GetDoubleNullable(0),
8);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetGuid(0), 16);
+ Test(() => new BinaryTupleReader(bytes, 1).GetGuidNullable(0), 16);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetDate(0), 7);
+ Test(() => new BinaryTupleReader(bytes, 1).GetDateNullable(0), 7);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetTime(0), 6);
+ Test(() => new BinaryTupleReader(bytes, 1).GetTimeNullable(0), 6);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetDateTime(0), 9);
+ Test(() => new BinaryTupleReader(bytes, 1).GetDateTimeNullable(0),
9);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetPeriod(0), 12);
+ Test(() => new BinaryTupleReader(bytes, 1).GetPeriodNullable(0),
12);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetDuration(0), 12);
+ Test(() => new BinaryTupleReader(bytes, 1).GetDurationNullable(0),
12);
+
+ Test(() => new BinaryTupleReader(bytes, 1).GetTimestamp(0), 12);
+ Test(() => new BinaryTupleReader(bytes,
1).GetTimestampNullable(0), 12);
+
+ static void Test(TestDelegate testDelegate, int expectedLength)
+ {
+ var ex =
Assert.Throws<InvalidOperationException>(testDelegate);
+
+ Assert.AreEqual(
+ $"Binary tuple element with index 0 has invalid length
(expected {expectedLength}, actual 1000).",
+ ex!.Message);
+ }
+ }
+
private static BinaryTupleReader BuildAndRead(BinaryTupleBuilderAction
build, int numElements = 1)
{
var bytes = Build(build, numElements);
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
index 91fced4448..e33ff2b83a 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
@@ -99,7 +99,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public sbyte GetByte(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s => unchecked((sbyte)s[0])
+ { Length: 1 } s => unchecked((sbyte)s[0]),
+ var s => throw GetInvalidLengthException(index, 1, s.Length)
};
/// <summary>
@@ -132,7 +133,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
{ IsEmpty: true } => default,
{ Length: 1 } s => unchecked((sbyte)s[0]),
- var s => BinaryPrimitives.ReadInt16LittleEndian(s)
+ { Length: 2 } s => BinaryPrimitives.ReadInt16LittleEndian(s),
+ var s => throw GetInvalidLengthException(index, 2, s.Length)
};
/// <summary>
@@ -152,7 +154,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{ IsEmpty: true } => default,
{ Length: 1 } s => unchecked((sbyte)s[0]),
{ Length: 2 } s => BinaryPrimitives.ReadInt16LittleEndian(s),
- var s => BinaryPrimitives.ReadInt32LittleEndian(s)
+ { Length: 4 } s => BinaryPrimitives.ReadInt32LittleEndian(s),
+ var s => throw GetInvalidLengthException(index, 4, s.Length)
};
/// <summary>
@@ -173,7 +176,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{ Length: 1 } s => unchecked((sbyte)s[0]),
{ Length: 2 } s => BinaryPrimitives.ReadInt16LittleEndian(s),
{ Length: 4 } s => BinaryPrimitives.ReadInt32LittleEndian(s),
- var s => BinaryPrimitives.ReadInt64LittleEndian(s)
+ { Length: 8 } s => BinaryPrimitives.ReadInt64LittleEndian(s),
+ var s => throw GetInvalidLengthException(index, 8, s.Length)
};
/// <summary>
@@ -191,7 +195,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public Guid GetGuid(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s => UuidSerializer.Read(s)
+ { Length: 16 } s => UuidSerializer.Read(s),
+ var s => throw GetInvalidLengthException(index, 16, s.Length)
};
/// <summary>
@@ -227,7 +232,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public float GetFloat(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s =>
BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(s))
+ { Length: 4 } s =>
BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(s)),
+ var s => throw GetInvalidLengthException(index, 4, s.Length)
};
/// <summary>
@@ -246,7 +252,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
{ IsEmpty: true } => default,
{ Length: 4 } s =>
BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(s)),
- var s =>
BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(s))
+ { Length: 8 } s =>
BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(s)),
+ var s => throw GetInvalidLengthException(index, 8, s.Length)
};
/// <summary>
@@ -320,7 +327,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public LocalDate GetDate(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s => ReadDate(s)
+ { Length: 3 } s => ReadDate(s),
+ var s => throw GetInvalidLengthException(index, 7, s.Length)
};
/// <summary>
@@ -338,7 +346,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public LocalTime GetTime(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s => ReadTime(s)
+ { Length: >= 4 and <= 6 } s => ReadTime(s),
+ var s => throw GetInvalidLengthException(index, 6, s.Length)
};
/// <summary>
@@ -356,7 +365,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public LocalDateTime GetDateTime(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s => ReadDate(s) + ReadTime(s[3..])
+ { Length: >= 7 and <= 9 } s => ReadDate(s) + ReadTime(s[3..]),
+ var s => throw GetInvalidLengthException(index, 9, s.Length)
};
/// <summary>
@@ -374,9 +384,10 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public Instant GetTimestamp(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s => Instant
- .FromUnixTimeSeconds(BinaryPrimitives.ReadInt64LittleEndian(s))
- .PlusNanoseconds(s.Length == 8 ? 0 :
BinaryPrimitives.ReadInt32LittleEndian(s[8..]))
+ { Length: 8 } s =>
Instant.FromUnixTimeSeconds(BinaryPrimitives.ReadInt64LittleEndian(s)),
+ { Length: 12 } s =>
Instant.FromUnixTimeSeconds(BinaryPrimitives.ReadInt64LittleEndian(s))
+
.PlusNanoseconds(BinaryPrimitives.ReadInt32LittleEndian(s[8..])),
+ var s => throw GetInvalidLengthException(index, 12, s.Length)
};
/// <summary>
@@ -394,9 +405,10 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public Duration GetDuration(int index) => Seek(index) switch
{
{ IsEmpty: true } => default,
- var s => Duration
- .FromSeconds(BinaryPrimitives.ReadInt64LittleEndian(s))
- .Plus(Duration.FromNanoseconds(s.Length == 8 ? 0 :
BinaryPrimitives.ReadInt32LittleEndian(s[8..])))
+ { Length: 8 } s =>
Duration.FromSeconds(BinaryPrimitives.ReadInt64LittleEndian(s)),
+ { Length: 12 } s =>
Duration.FromSeconds(BinaryPrimitives.ReadInt64LittleEndian(s))
+
.Plus(Duration.FromNanoseconds(BinaryPrimitives.ReadInt32LittleEndian(s[8..]))),
+ var s => throw GetInvalidLengthException(index, 12, s.Length)
};
/// <summary>
@@ -420,9 +432,10 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{ Length: 6 } s =>
Period.FromYears(BinaryPrimitives.ReadInt16LittleEndian(s)) +
Period.FromMonths(BinaryPrimitives.ReadInt16LittleEndian(s[2..])) +
Period.FromDays(BinaryPrimitives.ReadInt16LittleEndian(s[4..])),
- var s =>
Period.FromYears(BinaryPrimitives.ReadInt32LittleEndian(s)) +
-
Period.FromMonths(BinaryPrimitives.ReadInt32LittleEndian(s[4..])) +
-
Period.FromDays(BinaryPrimitives.ReadInt32LittleEndian(s[8..]))
+ { Length: 12 } s =>
Period.FromYears(BinaryPrimitives.ReadInt32LittleEndian(s)) +
+
Period.FromMonths(BinaryPrimitives.ReadInt32LittleEndian(s[4..])) +
+
Period.FromDays(BinaryPrimitives.ReadInt32LittleEndian(s[8..])),
+ var s => throw GetInvalidLengthException(index, 12, s.Length)
};
/// <summary>
@@ -566,6 +579,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
private static InvalidOperationException GetNullElementException(int
index) =>
new($"Binary tuple element with index {index} is null.");
+ private static InvalidOperationException GetInvalidLengthException(int
index, int expectedLength, int actualLength) =>
+ new($"Binary tuple element with index {index} has invalid length
(expected {expectedLength}, actual {actualLength}).");
+
private int GetOffset(int position)
{
var span = _buffer[position..];