From 454a49127f9416f45221eecf311eefdca50e4cdc Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Sat, 18 May 2024 19:39:59 +0200 Subject: [PATCH] Improved Long-Double Number Policy (#2674) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improved Long-Double Number Policy The Parsing of a Double value was always executing a `Long.parseLong(value)`, which generated a `NumberFormatException`. Identifying that a Number is a Double or a Long can be easily achieve (in a naive way) looking for the decimal separator. This simple change avoids the extra `NumberFormatException` A simple JUnit test, parsing a `Long` or a `Double` 10K times shows the next values: * Double (old parsing): ~42 ms * Double (new parsing): ~6 ms * Long (old parsing): ~7 ms * Long (new parsing): ~7 ms As we can see, the parsing for `Long` values stays the same (±1ms), while the parsing for `Double` is dramatically improved. Reducing the number of exceptions also has a positive side effect in memory consumption. * Replace `contains(".")` by `indexOf('.') >= 0` The usage of `indexOf(char)` is slightly faster * Rename exception variables --- .../java/com/google/gson/ToNumberPolicy.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/gson/src/main/java/com/google/gson/ToNumberPolicy.java b/gson/src/main/java/com/google/gson/ToNumberPolicy.java index 6380e5d986..7e9f85d0ca 100644 --- a/gson/src/main/java/com/google/gson/ToNumberPolicy.java +++ b/gson/src/main/java/com/google/gson/ToNumberPolicy.java @@ -68,22 +68,30 @@ public Number readNumber(JsonReader in) throws IOException { @Override public Number readNumber(JsonReader in) throws IOException, JsonParseException { String value = in.nextString(); - try { - return Long.parseLong(value); - } catch (NumberFormatException longE) { + if (value.indexOf('.') >= 0) { + return parseAsDouble(value, in); + } else { try { - Double d = Double.valueOf(value); - if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) { - throw new MalformedJsonException( - "JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); - } - return d; - } catch (NumberFormatException doubleE) { - throw new JsonParseException( - "Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); + return Long.parseLong(value); + } catch (NumberFormatException e) { + return parseAsDouble(value, in); } } } + + private Number parseAsDouble(String value, JsonReader in) throws IOException { + try { + Double d = Double.valueOf(value); + if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) { + throw new MalformedJsonException( + "JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); + } + return d; + } catch (NumberFormatException e) { + throw new JsonParseException( + "Cannot parse " + value + "; at path " + in.getPreviousPath(), e); + } + } }, /**