From 68af1dbe3d4bdd6ad40d52318b75f483c8b174a8 Mon Sep 17 00:00:00 2001 From: salyh Date: Fri, 11 Jul 2014 22:27:32 +0200 Subject: [PATCH 05/14] optimize parser for speeup parsing numbers and make getInt() and getLong() compliant with API. Signed-off-by: salyh --- .../apache/fleece/core/JsonBaseStreamParser.java | 61 +++++-- .../org/apache/fleece/core/JsonParserTest.java | 192 +++++++++++++++++++++ fleece-core/src/test/resources/json/numbers.json | 18 ++ 3 files changed, 255 insertions(+), 16 deletions(-) create mode 100644 fleece-core/src/test/resources/json/numbers.json diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java index bd29c33..4e98e5f 100644 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java +++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java @@ -52,6 +52,10 @@ public abstract class JsonBaseStreamParser implements JsonChars, private boolean constructingStringValue = false; private boolean withinArray = false; private boolean stringValueIsKey = false; + + private boolean isCurrentNumberIntegral = false; + private Integer currentIntegralNumber = null; //for number from 0 - 9 + private BigDecimal currentBigDecimalNumber = null; private int openObjects = 0; private int openArrays = 0; @@ -215,6 +219,9 @@ public abstract class JsonBaseStreamParser implements JsonChars, int dosCount = 0; lastEvent = event; event = null; + isCurrentNumberIntegral = false; + currentBigDecimalNumber = null; + currentIntegralNumber = null; resetValue(); @@ -609,6 +616,7 @@ public abstract class JsonBaseStreamParser implements JsonChars, boolean endExpected = false; final boolean zeropassed = c == '0'; + final boolean beginningMinusPassed = c == '-'; boolean dotpassed = false; boolean epassed = false; char last = c; @@ -627,7 +635,20 @@ public abstract class JsonBaseStreamParser implements JsonChars, if (n == COMMA || n == END_ARRAY_CHAR || n == END_OBJECT_CHAR) { resetToMark(); - + + isCurrentNumberIntegral=(!dotpassed && !epassed); + + if(isCurrentNumberIntegral && beginningMinusPassed && i==1 && last >= '0' && last <= '9') + { + currentIntegralNumber=-((int)last - 48); //optimize -0 till -99 + } + + if(isCurrentNumberIntegral && !beginningMinusPassed && i==0 && last >= '0' && last <= '9') + { + currentIntegralNumber=((int)last - 48); //optimize 0 till 9 + } + + event = Event.VALUE_NUMBER; break; } @@ -735,41 +756,49 @@ public abstract class JsonBaseStreamParser implements JsonChars, if (event != Event.VALUE_NUMBER) { throw new IllegalStateException(event - + " doesn't supportisIntegralNumber()"); - } - - for (int i = 0; i < valueLength; i++) { - if (!isAsciiDigit(currentValue[i])) { - return false; - } + + " doesn't support isIntegralNumber()"); } - return true; + return isCurrentNumberIntegral; } @Override public int getInt() { if (event != Event.VALUE_NUMBER) { - throw new IllegalStateException(event + " doesn't supportgetInt()"); + throw new IllegalStateException(event + " doesn't support getInt()"); + } + + if (isCurrentNumberIntegral && currentIntegralNumber != null) { + return currentIntegralNumber.intValue(); } - return Integer.parseInt(getValue()); + + return getBigDecimal().intValue(); } @Override public long getLong() { if (event != Event.VALUE_NUMBER) { - throw new IllegalStateException(event + " doesn't supporgetLong()"); + throw new IllegalStateException(event + " doesn't support getLong()"); } - return Long.parseLong(getValue()); + + if (isCurrentNumberIntegral && currentIntegralNumber != null) { + return currentIntegralNumber.intValue(); + } // int is ok, its only from 0-9 + + return getBigDecimal().longValue(); } @Override public BigDecimal getBigDecimal() { if (event != Event.VALUE_NUMBER) { - throw new IllegalStateException(event - + " doesn't support getBigDecimal()"); + throw new IllegalStateException(event + " doesn't support getBigDecimal()"); + } + + if (currentBigDecimalNumber != null) { + return currentBigDecimalNumber; } - return new BigDecimal(getValue()); + + return (currentBigDecimalNumber = new BigDecimal(getString())); } @Override diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java index 91ca272..a4b6418 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java @@ -305,6 +305,198 @@ public class JsonParserTest { parser.close(); } + + @Test + public void numbers() { + final JsonParser parser = Json.createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/numbers.json")); + assertNotNull(parser); + parser.next(); + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(0, parser.getInt()); + assertEquals(0, parser.getLong()); + assertEquals(new BigDecimal(0), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(0, parser.getInt()); + assertEquals(0, parser.getLong()); + assertEquals(new BigDecimal(0), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(1, parser.getInt()); + assertEquals(1, parser.getLong()); + assertEquals(new BigDecimal(1), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(-1, parser.getInt()); + assertEquals(-1L, parser.getLong()); + assertEquals(new BigDecimal(-1), parser.getBigDecimal()); + } + + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(9, parser.getInt()); + assertEquals(9L, parser.getLong()); + assertEquals(new BigDecimal(9), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(-9, parser.getInt()); + assertEquals(-9, parser.getLong()); + assertEquals(new BigDecimal(-9), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(10, parser.getInt()); + assertEquals(10, parser.getLong()); + assertEquals(new BigDecimal(10), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(-10, parser.getInt()); + assertEquals(-10, parser.getLong()); + assertEquals(new BigDecimal(-10), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(100, parser.getInt()); + assertEquals(100, parser.getLong()); + assertEquals(new BigDecimal(100), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(-100, parser.getInt()); + assertEquals(-100, parser.getLong()); + assertEquals(new BigDecimal(-100), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(456, parser.getInt()); + assertEquals(456, parser.getLong()); + assertEquals(new BigDecimal(456), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + assertEquals(-456, parser.getInt()); + assertEquals(-456, parser.getLong()); + assertEquals(new BigDecimal(-456), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(!parser.isIntegralNumber()); + assertEquals(123, parser.getInt()); + assertEquals(123, parser.getLong()); + assertEquals(new BigDecimal("123.12345"), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(!parser.isIntegralNumber()); + assertEquals(-123, parser.getInt()); + assertEquals(-123, parser.getLong()); + assertEquals(new BigDecimal("-123.12345"), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + //assertEquals(Integer.MAX_VALUE, parser.getInt()); + //assertEquals(Long.MAX_VALUE, parser.getLong()); + assertEquals(new BigDecimal("999999999999999999999999999999"), parser.getBigDecimal()); + } + parser.next(); + { + assertTrue(parser.hasNext()); + final JsonParser.Event event = parser.next(); + assertNotNull(event); + assertEquals(JsonParser.Event.VALUE_NUMBER, event); + assertTrue(parser.isIntegralNumber()); + //assertEquals(Integer.MIN_VALUE, parser.getInt()); + //assertEquals(Long.MIN_VALUE, parser.getLong()); + assertEquals(new BigDecimal("-999999999999999999999999999999"), parser.getBigDecimal()); + } + parser.next(); + + { + assertFalse(parser.hasNext()); + } + parser.close(); + } + + @Test public void bigdecimal() { final JsonParser parser = Json.createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/bigdecimal.json")); diff --git a/fleece-core/src/test/resources/json/numbers.json b/fleece-core/src/test/resources/json/numbers.json new file mode 100644 index 0000000..d300022 --- /dev/null +++ b/fleece-core/src/test/resources/json/numbers.json @@ -0,0 +1,18 @@ +{ + "0": 0, + "0": -0, + "1": 1, + "-1": -1, + "9": 9, + "-9": -9, + "10": 10, + "-10", -10, + "100": 100, + "-100", -100, + "456": 456, + "-456": -456, + "123.12345": 123.12345, + "-123.12345": -123.12345, + "999999999999999999999999999999": 999999999999999999999999999999, + "-999999999999999999999999999999": -999999999999999999999999999999 +} \ No newline at end of file -- 1.8.5.2 (Apple Git-48)