Skip to content

Commit

Permalink
Ensure values written for int schemas don't exceed int32 bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
dstelljes committed Feb 11, 2022
1 parent 56a7bbc commit db1364a
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ public class BinaryIntSerializerBuilderCase : IntSerializerBuilderCase, IBinaryS
/// otherwise.
/// </returns>
/// <exception cref="UnsupportedTypeException">
/// Thrown when <paramref name="type" /> cannot be converted to <see cref="long" />.
/// Thrown when <paramref name="type" /> cannot be converted to <see cref="int" />.
/// </exception>
/// <inheritdoc />
public virtual BinarySerializerBuilderCaseResult BuildExpression(Expression value, Type type, Schema schema, BinarySerializerBuilderContext context)
{
if (schema is IntSchema intSchema)
{
var writeInteger = typeof(BinaryWriter)
.GetMethod(nameof(BinaryWriter.WriteInteger), new[] { typeof(long) });
.GetMethod(nameof(BinaryWriter.WriteInteger), new[] { typeof(int) });

try
{
return BinarySerializerBuilderCaseResult.FromExpression(
Expression.Call(context.Writer, writeInteger, BuildConversion(value, typeof(long))));
Expression.Call(context.Writer, writeInteger, BuildConversion(value, typeof(int))));
}
catch (InvalidOperationException exception)
{
Expand Down
25 changes: 25 additions & 0 deletions src/Chr.Avro.Binary/Serialization/BinaryWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ public void WriteFixed(byte[] value)
stream.Write(value, 0, value.Length);
}

/// <summary>
/// Writes a variable-length integer to the current position and advances the writer.
/// </summary>
/// <param name="value">
/// An <see cref="int" /> value.
/// </param>
public void WriteInteger(int value)
{
var encoded = (uint)((value << 1) ^ (value >> 31));

do
{
var current = encoded & 0x7FU;
encoded >>= 7;

if (encoded != 0)
{
current |= 0x80U;
}

stream.WriteByte((byte)current);
}
while (encoded != 0U);
}

/// <summary>
/// Writes a variable-length integer to the current position and advances the writer.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class JsonIntSerializerBuilderCase : IntSerializerBuilderCase, IJsonSeria
/// otherwise.
/// </returns>
/// <exception cref="UnsupportedTypeException">
/// Thrown when <paramref name="type" /> cannot be converted to <see cref="long" />.
/// Thrown when <paramref name="type" /> cannot be converted to <see cref="int" />.
/// </exception>
/// <inheritdoc />
public virtual JsonSerializerBuilderCaseResult BuildExpression(Expression value, Type type, Schema schema, JsonSerializerBuilderContext context)
Expand Down
43 changes: 31 additions & 12 deletions tests/Chr.Avro.Binary.Tests/BinaryWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,23 @@ public BinaryWriterTests()
new object[] { double.PositiveInfinity, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f } },
};

public static IEnumerable<object[]> IntegerEncodings => new List<object[]>
public static IEnumerable<object[]> Int32Encodings => new List<object[]>
{
new object[] { 0, new byte[] { 0x00 } },
new object[] { -1, new byte[] { 0x01 } },
new object[] { 1, new byte[] { 0x02 } },
new object[] { -2, new byte[] { 0x03 } },
new object[] { 2, new byte[] { 0x04 } },
new object[] { -64, new byte[] { 0x7f } },
new object[] { 64, new byte[] { 0x80, 0x01 } },
new object[] { -8192, new byte[] { 0xff, 0x7f } },
new object[] { 8192, new byte[] { 0x80, 0x80, 0x01 } },
new object[] { int.MinValue, new byte[] { 0xff, 0xff, 0xff, 0xff, 0x0f } },
new object[] { int.MaxValue, new byte[] { 0xfe, 0xff, 0xff, 0xff, 0x0f } },
};

public static IEnumerable<object[]> Int64Encodings => new List<object[]>
{
new object[] { 0L, new byte[] { 0x00 } },
new object[] { -1L, new byte[] { 0x01 } },
new object[] { 1L, new byte[] { 0x02 } },
new object[] { -2L, new byte[] { 0x03 } },
new object[] { 2L, new byte[] { 0x04 } },
new object[] { -64L, new byte[] { 0x7f } },
new object[] { 64L, new byte[] { 0x80, 0x01 } },
new object[] { -8192L, new byte[] { 0xff, 0x7f } },
new object[] { 8192L, new byte[] { 0x80, 0x80, 0x01 } },
new object[] { -4611686018427387904L, new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f } },
new object[] { 4611686018427387904L, new byte[] { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01 } },
new object[] { long.MinValue, new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 } },
Expand Down Expand Up @@ -108,8 +114,21 @@ public void WritesDoubles(double value, byte[] encoding)
}

[Theory]
[MemberData(nameof(IntegerEncodings))]
public void WritesIntegers(long value, byte[] encoding)
[MemberData(nameof(Int32Encodings))]
public void WritesInt32s(int value, byte[] encoding)
{
using (stream)
{
writer.WriteInteger(value);
}

Assert.Equal(encoding, stream.ToArray());
}

[Theory]
[MemberData(nameof(Int32Encodings))]
[MemberData(nameof(Int64Encodings))]
public void WritesInt64s(long value, byte[] encoding)
{
using (stream)
{
Expand Down
4 changes: 2 additions & 2 deletions tests/Chr.Avro.Binary.Tests/IntegerSerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public IntegerSerializationTests()
public static IEnumerable<object[]> UInt32s => new List<object[]>
{
new object[] { uint.MinValue },
new object[] { uint.MaxValue },
new object[] { uint.MaxValue / 2 },
};

public static IEnumerable<object[]> UInt64s => new List<object[]>
Expand Down Expand Up @@ -296,7 +296,7 @@ public void UInt32Values(uint value)
[MemberData(nameof(UInt64s))]
public void UInt64Values(ulong value)
{
var schema = new IntSchema();
var schema = new LongSchema();

var deserialize = deserializerBuilder.BuildDelegate<ulong>(schema);
var serialize = serializerBuilder.BuildDelegate<ulong>(schema);
Expand Down

0 comments on commit db1364a

Please sign in to comment.