You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2020/02/28 13:16:12 UTC
[commons-jexl] branch master updated: JEXL-277: fast path basic
arithmetic ops (+, -, */, %) on primitive relative numbers (long, int,
short...), check overflows Task #JEXL-277 - Speedup arithmetic evaluations
This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push:
new 94df556 JEXL-277: fast path basic arithmetic ops (+,-,*/,%) on primitive relative numbers (long, int, short...), check overflows Task #JEXL-277 - Speedup arithmetic evaluations
94df556 is described below
commit 94df556911b7df57339c30d31a52fa7b673df77e
Author: henrib <he...@apache.org>
AuthorDate: Fri Feb 28 14:15:13 2020 +0100
JEXL-277: fast path basic arithmetic ops (+,-,*/,%) on primitive relative numbers (long, int, short...), check overflows
Task #JEXL-277 - Speedup arithmetic evaluations
---
.../org/apache/commons/jexl3/JexlArithmetic.java | 98 +++++++++++-----------
.../org/apache/commons/jexl3/ArithmeticTest.java | 5 +-
2 files changed, 51 insertions(+), 52 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 89b602f..61335f9 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -25,7 +25,6 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Collection;
-import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
@@ -635,7 +634,7 @@ public class JexlArithmetic {
}
return bigd;
}
-
+
/**
* Replace all numbers in an arguments array with the smallest type that will fit.
*
@@ -660,25 +659,36 @@ public class JexlArithmetic {
}
return narrowed;
}
-
+
/**
- * Checks if value class is a number that can be represented exactly in an int.
- *
- * @param value argument
- * @return true if argument can be represented by an integer
+ * Given a long, attempt to narrow it to an int.
+ * <p>Narrowing will only occur if no operand is a Long.
+ * @param lhs the left hand side operand that lead to the long result
+ * @param rhs the right hand side operand that lead to the long result
+ * @param r the long to narrow
+ * @return an Integer if narrowing is possible, the original Long otherwise
*/
- private static Number asIntegerNumber(Object value) {
- return value instanceof Integer || value instanceof Short || value instanceof Byte ? (Number) value : null;
+ protected Number narrowLong(Object lhs, Object rhs, long r) {
+ if (!(lhs instanceof Long || rhs instanceof Long) && (int) r == r) {
+ return (int) r;
+ } else {
+ return r;
+ }
}
-
+
/**
* Checks if value class is a number that can be represented exactly in a long.
*
* @param value argument
* @return true if argument can be represented by a long
*/
- private static Number asLongNumber(Object value) {
- return value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte ? (Number) value : null;
+ protected Number asLongNumber(Object value) {
+ return value instanceof Long
+ || value instanceof Integer
+ || value instanceof Short
+ || value instanceof Byte
+ ? (Number) value
+ : null;
}
/**
@@ -707,15 +717,12 @@ public class JexlArithmetic {
if (ln != null && rn != null) {
long x = ln.longValue();
long y = rn.longValue();
- long r = x + y;
+ long result = x + y;
// detect overflow, see java8 Math.addExact
- if (((x ^ r) & (y ^ r)) < 0) {
+ if (((x ^ result) & (y ^ result)) < 0) {
return BigInteger.valueOf(x).add(BigInteger.valueOf(y));
}
- if (!(left instanceof Long || right instanceof Long) && (int) r == r) {
- return (int) r;
- }
- return r;
+ return narrowLong(left, right, result);
}
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
@@ -760,16 +767,13 @@ public class JexlArithmetic {
Number ln = asLongNumber(left);
Number rn = asLongNumber(right);
if (ln != null && rn != null) {
- long l = ln.longValue();
- long r = rn.longValue();
- if (r == 0L) {
+ long x = ln.longValue();
+ long y = rn.longValue();
+ if (y == 0L) {
throw new ArithmeticException("/");
}
- long result = l / r;
- if (!(left instanceof Long || right instanceof Long) && (int) result == result) {
- return (int) result;
- }
- return result;
+ long result = x / y;
+ return narrowLong(left, right, result);
}
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
@@ -816,16 +820,13 @@ public class JexlArithmetic {
Number ln = asLongNumber(left);
Number rn = asLongNumber(right);
if (ln != null && rn != null) {
- long l = ln.longValue();
- long r = rn.longValue();
- if (r == 0L) {
+ long x = ln.longValue();
+ long y = rn.longValue();
+ if (y == 0L) {
throw new ArithmeticException("%");
}
- long result = l % r;
- if (!(left instanceof Long || right instanceof Long) && (int) result == result) {
- return (int) result;
- }
- return result;
+ long result = x % y;
+ return narrowLong(left, right, result);
}
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
@@ -864,12 +865,13 @@ public class JexlArithmetic {
* @param r the product
* @return true if product fits a long, false if it overflows
*/
+ @SuppressWarnings("MagicNumber")
private static boolean isMultiplyExact(long x, long y, long r) {
long ax = Math.abs(x);
long ay = Math.abs(y);
- return !(((ax | ay) >>> 31 != 0) &&
- (((y != 0) && (r / y != x)) ||
- (x == Long.MIN_VALUE && y == -1)));
+ return !(((ax | ay) >>> (Integer.SIZE - 1) != 0)
+ && (((y != 0) && (r / y != x))
+ || (x == Long.MIN_VALUE && y == -1)));
}
/**
@@ -884,20 +886,17 @@ public class JexlArithmetic {
return controlNullNullOperands();
}
// if both (non null) args fit as int
- Number ln = asIntegerNumber(left);
- Number rn = asIntegerNumber(right);
+ Number ln = asLongNumber(left);
+ Number rn = asLongNumber(right);
if (ln != null && rn != null) {
long x = ln.longValue();
long y = rn.longValue();
- long r = x * y;
+ long result = x * y;
// detect overflow
- if (!isMultiplyExact(x, y, r)) {
+ if (!isMultiplyExact(x, y, result)) {
return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y));
}
- if (!(left instanceof Long || right instanceof Long) && (int) r == r) {
- return (int) r;
- }
- return r;
+ return narrowLong(left, right, result);
}
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
@@ -936,15 +935,12 @@ public class JexlArithmetic {
if (ln != null && rn != null) {
long x = ln.longValue();
long y = rn.longValue();
- long r = x - y;
+ long result = x - y;
// detect overflow, see java8 Math.subtractExact
- if (((x ^ y) & (x ^ r)) < 0) {
+ if (((x ^ y) & (x ^ result)) < 0) {
return BigInteger.valueOf(x).subtract(BigInteger.valueOf(y));
}
- if ((int) r == r) {
- return (int) r;
- }
- return r;
+ return narrowLong(left, right, result);
}
// if either are bigdecimal use that type
if (left instanceof BigDecimal || right instanceof BigDecimal) {
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 245f64d..3c6be95 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -132,11 +132,14 @@ public class ArithmeticTest extends JexlTestCase {
@Test
public void testOverflows() throws Exception {
asserter.assertExpression("1 + 2147483647", Long.valueOf("2147483648"));
+ asserter.assertExpression("3 + " + (Long.MAX_VALUE - 2), BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE));
asserter.assertExpression("-2147483648 - 1", Long.valueOf("-2147483649"));
+ asserter.assertExpression("-3 + " + (Long.MIN_VALUE + 2), BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE));
asserter.assertExpression("1 + 9223372036854775807", new BigInteger("9223372036854775808"));
asserter.assertExpression("-1 + (-9223372036854775808)", new BigInteger("-9223372036854775809"));
asserter.assertExpression("-9223372036854775808 - 1", new BigInteger("-9223372036854775809"));
- asserter.assertExpression("-1 - 9223372036854775808", new BigInteger("-9223372036854775809"));
+ BigInteger maxl = BigInteger.valueOf(Long.MAX_VALUE);
+ asserter.assertExpression(maxl.toString() + " * " + maxl.toString() , maxl.multiply(maxl));
}
/**