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..];

Reply via email to