From e1755333dac8751d94b866dac792b5afe2d6d07d Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Thu, 9 May 2024 14:05:09 +0200 Subject: [PATCH 1/3] Improved Long-Double Number Policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../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..0cd17ff3b7 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.contains(".")) { + 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 longE) { + 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 doubleE) { + throw new JsonParseException( + "Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); + } + } }, /** From aa2db76800fbe889ea419cc990b3faf8c3bdae4c Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Mon, 13 May 2024 16:34:48 +0200 Subject: [PATCH 2/3] Replace `contains(".")` by `indexOf('.') >= 0` The usage of `indexOf(char)` is slightly faster --- gson/src/main/java/com/google/gson/ToNumberPolicy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gson/src/main/java/com/google/gson/ToNumberPolicy.java b/gson/src/main/java/com/google/gson/ToNumberPolicy.java index 0cd17ff3b7..64c5a4251c 100644 --- a/gson/src/main/java/com/google/gson/ToNumberPolicy.java +++ b/gson/src/main/java/com/google/gson/ToNumberPolicy.java @@ -68,7 +68,7 @@ public Number readNumber(JsonReader in) throws IOException { @Override public Number readNumber(JsonReader in) throws IOException, JsonParseException { String value = in.nextString(); - if (value.contains(".")) { + if (value.indexOf('.') >= 0) { return parseAsDouble(value, in); } else { try { From cf87bf02335586f130e0db529ba9e7a6a4a506ba Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Tue, 14 May 2024 08:38:55 +0200 Subject: [PATCH 3/3] Rename exception variables --- gson/src/main/java/com/google/gson/ToNumberPolicy.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gson/src/main/java/com/google/gson/ToNumberPolicy.java b/gson/src/main/java/com/google/gson/ToNumberPolicy.java index 64c5a4251c..7e9f85d0ca 100644 --- a/gson/src/main/java/com/google/gson/ToNumberPolicy.java +++ b/gson/src/main/java/com/google/gson/ToNumberPolicy.java @@ -73,7 +73,7 @@ public Number readNumber(JsonReader in) throws IOException, JsonParseException { } else { try { return Long.parseLong(value); - } catch (NumberFormatException longE) { + } catch (NumberFormatException e) { return parseAsDouble(value, in); } } @@ -87,9 +87,9 @@ private Number parseAsDouble(String value, JsonReader in) throws IOException { "JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); } return d; - } catch (NumberFormatException doubleE) { + } catch (NumberFormatException e) { throw new JsonParseException( - "Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); + "Cannot parse " + value + "; at path " + in.getPreviousPath(), e); } } },