Repository: ignite Updated Branches: refs/heads/ignite-2.0 1410900f2 -> 19739a080
IGNITE-3196 Add support for BigDecimals with negative scale in BinaryMarshaller This closes #1473 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/19739a08 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/19739a08 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/19739a08 Branch: refs/heads/ignite-2.0 Commit: 19739a080d389658878cd8eea5f28ce7dc5a229e Parents: 1410900 Author: Vyacheslav Daradur <[email protected]> Authored: Fri Feb 10 16:51:37 2017 +0300 Committer: Pavel Tupitsyn <[email protected]> Committed: Fri Feb 10 16:51:37 2017 +0300 ---------------------------------------------------------------------- .../ignite/internal/binary/BinaryUtils.java | 12 +++--- .../internal/binary/BinaryWriterExImpl.java | 14 ++++--- .../binary/BinaryMarshallerSelfTest.java | 30 +++++++++++++ modules/platforms/cpp/odbc/src/utility.cpp | 17 +++++--- .../Compute/ComputeApiTest.cs | 18 ++++---- .../Impl/Binary/BinaryUtils.cs | 44 ++++++++++++-------- 6 files changed, 92 insertions(+), 43 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/19739a08/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java index 1153d15..6831ef9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java @@ -65,8 +65,8 @@ import org.apache.ignite.lang.IgniteUuid; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentHashMap8; -import static org.apache.ignite.IgniteSystemProperties.IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.ignite.IgniteSystemProperties.IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2; /** * Binary utils. @@ -1193,13 +1193,15 @@ public class BinaryUtils { int scale = in.readInt(); byte[] mag = doReadByteArray(in); - BigInteger intVal = new BigInteger(mag); + boolean negative = mag[0] < 0; - if (scale < 0) { - scale &= 0x7FFFFFFF; + if (negative) + mag[0] &= 0x7F; + BigInteger intVal = new BigInteger(mag); + + if (negative) intVal = intVal.negate(); - } return new BigDecimal(intVal, scale); } http://git-wip-us.apache.org/repos/asf/ignite/blob/19739a08/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java index adaacdd..3289780 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryWriterExImpl.java @@ -403,18 +403,20 @@ public class BinaryWriterExImpl implements BinaryWriter, BinaryRawWriterEx, Obje out.unsafeWriteByte(GridBinaryMarshaller.DECIMAL); + out.unsafeWriteInt(val.scale()); + BigInteger intVal = val.unscaledValue(); - if (intVal.signum() == -1) { - intVal = intVal.negate(); + boolean negative = intVal.signum() == -1; - out.unsafeWriteInt(val.scale() | 0x80000000); - } - else - out.unsafeWriteInt(val.scale()); + if (negative) + intVal = intVal.negate(); byte[] vals = intVal.toByteArray(); + if (negative) + vals[0] |= -0x80; + out.unsafeWriteInt(vals.length); out.writeByteArray(vals); } http://git-wip-us.apache.org/repos/asf/ignite/blob/19739a08/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java index 003a330..2d43559 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/binary/BinaryMarshallerSelfTest.java @@ -29,6 +29,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; import java.net.InetSocketAddress; import java.sql.Timestamp; import java.util.AbstractQueue; @@ -183,6 +184,35 @@ public class BinaryMarshallerSelfTest extends GridCommonAbstractTest { assertEquals((val = new BigDecimal(new BigInteger("-79228162514264337593543950336"))), marshalUnmarshal(val)); } + + /** + * @throws Exception If failed. + */ + public void testNegativeScaleDecimal() throws Exception { + BigDecimal val; + + assertEquals((val = BigDecimal.valueOf(Long.MAX_VALUE, -1)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Long.MIN_VALUE, -2)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Long.MAX_VALUE, -3)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Long.MIN_VALUE, -4)), marshalUnmarshal(val)); + } + + /** + * @throws Exception If failed. + */ + public void testNegativeScaleRoundingModeDecimal() throws Exception { + BigDecimal val; + + assertEquals((val = BigDecimal.ZERO.setScale(-1, RoundingMode.HALF_UP)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Long.MAX_VALUE).setScale(-3, RoundingMode.HALF_DOWN)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Long.MIN_VALUE).setScale(-5, RoundingMode.HALF_EVEN)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Integer.MAX_VALUE).setScale(-8, RoundingMode.UP)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Integer.MIN_VALUE).setScale(-10, RoundingMode.DOWN)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Double.MAX_VALUE).setScale(-12, RoundingMode.CEILING)), marshalUnmarshal(val)); + assertEquals((val = BigDecimal.valueOf(Double.MIN_VALUE).setScale(-15, RoundingMode.FLOOR)), marshalUnmarshal(val)); + } + + /** * @throws Exception If failed. */ http://git-wip-us.apache.org/repos/asf/ignite/blob/19739a08/modules/platforms/cpp/odbc/src/utility.cpp ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/odbc/src/utility.cpp b/modules/platforms/cpp/odbc/src/utility.cpp index 63454dc..3222a38 100644 --- a/modules/platforms/cpp/odbc/src/utility.cpp +++ b/modules/platforms/cpp/odbc/src/utility.cpp @@ -83,8 +83,14 @@ namespace ignite impl::binary::BinaryUtils::ReadInt8Array(reader.GetStream(), mag.data(), static_cast<int32_t>(mag.size())); - int32_t sign = (scale & 0x80000000) ? -1 : 1; - scale = scale & 0x7FFFFFFF; + int32_t sign = 1; + + if (mag[0] < 0) + { + mag[0] &= 0x7F; + + sign = -1; + } common::Decimal res(mag.data(), static_cast<int32_t>(mag.size()), scale, sign); @@ -97,14 +103,15 @@ namespace ignite const common::BigInteger &unscaled = decimal.GetUnscaledValue(); - int32_t signFlag = unscaled.GetSign() == -1 ? 0x80000000 : 0; - - writer.WriteInt32(decimal.GetScale() | signFlag); + writer.WriteInt32(decimal.GetScale()); common::FixedSizeArray<int8_t> magnitude; unscaled.MagnitudeToBytes(magnitude); + if (unscaled.GetSign() == -1) + magnitude[0] |= -0x80; + writer.WriteInt32(magnitude.GetSize()); impl::binary::BinaryUtils::WriteInt8Array(writer.GetStream(), magnitude.GetData(), magnitude.GetSize()); http://git-wip-us.apache.org/repos/asf/ignite/blob/19739a08/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs index e2d3f61..da3ef1a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Compute/ComputeApiTest.cs @@ -823,16 +823,16 @@ namespace Apache.Ignite.Core.Tests.Compute Assert.AreEqual(val = decimal.Parse("-11,12"), _grid1.GetCompute().ExecuteJavaTask<object>(DecimalTask, new object[] { val, val.ToString() })); // Test echo with overflow. - try - { - _grid1.GetCompute().ExecuteJavaTask<object>(DecimalTask, new object[] { null, decimal.MaxValue.ToString() + 1 }); + var ex = Assert.Throws<BinaryObjectException>(() => _grid1.GetCompute() + .ExecuteJavaTask<object>(DecimalTask, new object[] {null, decimal.MaxValue.ToString() + 1})); - Assert.Fail(); - } - catch (IgniteException) - { - // No-op. - } + Assert.AreEqual("Decimal magnitude overflow (must be less than 96 bits): 104", ex.Message); + + // Negative scale. 1E+1 parses to "1 scale -1" on Java side. + ex = Assert.Throws<BinaryObjectException>(() => _grid1.GetCompute() + .ExecuteJavaTask<object>(DecimalTask, new object[] {null, "1E+1"})); + + Assert.AreEqual("Decimal value scale overflow (must be between 0 and 28): -1", ex.Message); } /// <summary> http://git-wip-us.apache.org/repos/asf/ignite/blob/19739a08/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs index e6a3716..09c3ad4 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs @@ -893,7 +893,9 @@ namespace Apache.Ignite.Core.Impl.Binary // Write scale and negative flag. int scale = (vals[3] & 0x00FF0000) >> 16; - stream.WriteInt(((vals[3] & 0x80000000) == 0x80000000) ? (int)((uint)scale | 0x80000000) : scale); + stream.WriteInt(scale); + + Boolean neg = vals[3] < 0; if (idx == -1) { @@ -923,13 +925,15 @@ namespace Apache.Ignite.Core.Impl.Binary if ((part24 & 0x80) == 0x80) { stream.WriteInt(len + 1); + + stream.WriteByte((byte)(neg ? -0x80 : ByteZero)); - stream.WriteByte(ByteZero); + neg = false; } else stream.WriteInt(len); - stream.WriteByte((byte)part24); + stream.WriteByte((byte)(neg ? ((sbyte)part24 | -0x80) : part24)); stream.WriteByte((byte)part16); stream.WriteByte((byte)part8); stream.WriteByte((byte)part0); @@ -940,12 +944,14 @@ namespace Apache.Ignite.Core.Impl.Binary { stream.WriteInt(len); - stream.WriteByte(ByteZero); + stream.WriteByte((byte)(neg ? -0x80 : ByteZero)); + + neg = false; } else stream.WriteInt(len - 1); - - stream.WriteByte((byte)part16); + + stream.WriteByte((byte)(neg ? ((sbyte)part16 | -0x80) : part16)); stream.WriteByte((byte)part8); stream.WriteByte((byte)part0); } @@ -955,12 +961,14 @@ namespace Apache.Ignite.Core.Impl.Binary { stream.WriteInt(len - 1); - stream.WriteByte(ByteZero); + stream.WriteByte((byte)(neg ? -0x80 : ByteZero)); + + neg = false; } else stream.WriteInt(len - 2); - - stream.WriteByte((byte)part8); + + stream.WriteByte((byte)(neg ? ((sbyte)part8 | -0x80) : part8)); stream.WriteByte((byte)part0); } else @@ -969,12 +977,14 @@ namespace Apache.Ignite.Core.Impl.Binary { stream.WriteInt(len - 2); - stream.WriteByte(ByteZero); + stream.WriteByte((byte)(neg ? -0x80 : ByteZero)); + + neg = false; } else stream.WriteInt(len - 3); - stream.WriteByte((byte)part0); + stream.WriteByte((byte)(neg ? ((sbyte)part0 | -0x80) : part0)); } } else @@ -997,18 +1007,16 @@ namespace Apache.Ignite.Core.Impl.Binary { int scale = stream.ReadInt(); - bool neg; + bool neg = false; + + byte[] mag = ReadByteArray(stream); - if (scale < 0) + if ((sbyte)mag[0] < 0) { - scale = scale & 0x7FFFFFFF; + mag[0] &= 0x7F; neg = true; } - else - neg = false; - - byte[] mag = ReadByteArray(stream); if (scale < 0 || scale > 28) throw new BinaryObjectException("Decimal value scale overflow (must be between 0 and 28): " + scale);
