Minor performance optimization for "untyped deserializer" (esp. small lists, maps); smile parser
diff --git a/src/mapper/java/org/codehaus/jackson/map/ObjectMapper.java b/src/mapper/java/org/codehaus/jackson/map/ObjectMapper.java
index c6bcb55..bfd18da 100644
--- a/src/mapper/java/org/codehaus/jackson/map/ObjectMapper.java
+++ b/src/mapper/java/org/codehaus/jackson/map/ObjectMapper.java
@@ -1779,7 +1779,6 @@
if (deser != null) {
return deser;
}
-
// Nope: need to ask provider to resolve it
deser = _deserializerProvider.findTypedValueDeserializer(cfg, valueType);
if (deser == null) { // can this happen?
diff --git a/src/mapper/java/org/codehaus/jackson/map/deser/UntypedObjectDeserializer.java b/src/mapper/java/org/codehaus/jackson/map/deser/UntypedObjectDeserializer.java
index 4de483e..507d930 100644
--- a/src/mapper/java/org/codehaus/jackson/map/deser/UntypedObjectDeserializer.java
+++ b/src/mapper/java/org/codehaus/jackson/map/deser/UntypedObjectDeserializer.java
@@ -8,9 +8,9 @@
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.DeserializationContext;
-import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.TypeDeserializer;
import org.codehaus.jackson.map.annotate.JacksonStdImpl;
+import org.codehaus.jackson.map.util.ObjectBuffer;
/**
* This deserializer is only used if it is necessary to bind content of
@@ -143,30 +143,67 @@
protected List<Object> mapArray(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
- ArrayList<Object> result = new ArrayList<Object>();
- while (jp.nextToken() != JsonToken.END_ARRAY) {
- result.add(deserialize(jp, ctxt));
+ // Minor optimization to handle small lists (default size for ArrayList is 10)
+ if (jp.nextToken() == JsonToken.END_ARRAY) {
+ return new ArrayList<Object>(4);
}
+ ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+ Object[] values = buffer.resetAndStart();
+ int ptr = 0;
+ int totalSize = 0;
+ do {
+ Object value = deserialize(jp, ctxt);
+ ++totalSize;
+ if (ptr >= values.length) {
+ values = buffer.appendCompletedChunk(values);
+ ptr = 0;
+ }
+ values[ptr++] = value;
+ } while (jp.nextToken() != JsonToken.END_ARRAY);
+ // let's create almost full array, with 1/8 slack
+ ArrayList<Object> result = new ArrayList<Object>(totalSize + (totalSize >> 3) + 1);
+ buffer.completeAndClearBuffer(values, ptr, result);
return result;
}
protected Map<String,Object> mapObject(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
- LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.START_OBJECT) {
t = jp.nextToken();
}
- for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+ // 1.6: minor optimization; let's handle 1 and 2 entry cases separately
+ if (t != JsonToken.FIELD_NAME) { // and empty one too
+ // empty map might work; but caller may want to modify... so better just give small modifiable
+ return new LinkedHashMap<String,Object>(4);
+ }
+ String field1 = jp.getText();
+ jp.nextToken();
+ Object value1 = deserialize(jp, ctxt);
+ if (jp.nextToken() != JsonToken.FIELD_NAME) { // single entry; but we want modifiable
+ LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
+ result.put(field1, value1);
+ return result;
+ }
+ String field2 = jp.getText();
+ jp.nextToken();
+ Object value2 = deserialize(jp, ctxt);
+ if (jp.nextToken() != JsonToken.FIELD_NAME) {
+ LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
+ result.put(field1, value1);
+ result.put(field2, value2);
+ return result;
+ }
+ // And then the general case; default map size is 16
+ LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
+ result.put(field1, value1);
+ result.put(field2, value2);
+ do {
String fieldName = jp.getText();
jp.nextToken();
result.put(fieldName, deserialize(jp, ctxt));
- }
- // sanity check
- if (t != JsonToken.END_OBJECT) {
- throw JsonMappingException.from(jp, "Unexpected token ("+t+"), expected END_OBJECT after JSON Object");
- }
+ } while (jp.nextToken() != JsonToken.END_OBJECT);
return result;
}
}
diff --git a/src/mapper/java/org/codehaus/jackson/map/util/ObjectBuffer.java b/src/mapper/java/org/codehaus/jackson/map/util/ObjectBuffer.java
index d0d7dea..2ebccad 100644
--- a/src/mapper/java/org/codehaus/jackson/map/util/ObjectBuffer.java
+++ b/src/mapper/java/org/codehaus/jackson/map/util/ObjectBuffer.java
@@ -1,6 +1,7 @@
package org.codehaus.jackson.map.util;
import java.lang.reflect.Array;
+import java.util.List;
/**
* Helper class to use for constructing Object arrays by appending entries
@@ -31,15 +32,15 @@
// // // Data storage
- Node _bufferHead;
+ private Node _bufferHead;
- Node _bufferTail;
+ private Node _bufferTail;
/**
* Number of total buffered entries in this buffer, counting all instances
* within linked list formed by following {@link #_bufferHead}.
*/
- int _bufferedEntryCount;
+ private int _bufferedEntryCount;
// // // Simple reuse
@@ -47,20 +48,20 @@
* Reusable Object array, stored here after buffer has been released having
* been used previously.
*/
- Object[] _freeBuffer;
+ private Object[] _freeBuffer;
/*
- ////////////////////////////////////////////////////////////////////////
- // Construction
- ////////////////////////////////////////////////////////////////////////
+ /**********************************************************
+ /* Construction
+ /**********************************************************
*/
public ObjectBuffer() { }
/*
- ////////////////////////////////////////////////////////////////////////
- // Public API
- ////////////////////////////////////////////////////////////////////////
+ /**********************************************************
+ /* Public API
+ /**********************************************************
*/
/**
@@ -147,6 +148,25 @@
}
/**
+ * Another
+ *
+ * @since 1.6
+ */
+ public void completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries, List<Object> resultList)
+ {
+ for (Node n = _bufferHead; n != null; n = n.next()) {
+ Object[] curr = n.getData();
+ for (int i = 0, len = curr.length; i < len; ++i) {
+ resultList.add(curr[i]);
+ }
+ }
+ // and then the last one
+ for (int i = 0; i < lastChunkEntries; ++i) {
+ resultList.add(lastChunk[i]);
+ }
+ }
+
+ /**
* Helper method that can be used to check how much free capacity
* will this instance start with. Can be used to choose the best
* instance to reuse, based on size of reusable object chunk
@@ -164,9 +184,9 @@
public int bufferedSize() { return _bufferedEntryCount; }
/*
- ////////////////////////////////////////////////////////////////////////
- // Internal methods
- ////////////////////////////////////////////////////////////////////////
+ /**********************************************************
+ /* Internal methods
+ /**********************************************************
*/
protected void _reset()
@@ -201,9 +221,9 @@
}
/*
- ////////////////////////////////////////////////////////////////////////
- // Helper classes
- ////////////////////////////////////////////////////////////////////////
+ /**********************************************************
+ /* Helper classes
+ /**********************************************************
*/
/**
diff --git a/src/perf/TestJsonPerf.java b/src/perf/TestJsonPerf.java
index 1bee97b..dfe49ee 100644
--- a/src/perf/TestJsonPerf.java
+++ b/src/perf/TestJsonPerf.java
@@ -59,7 +59,7 @@
while (true) {
try { Thread.sleep(100L); } catch (InterruptedException ie) { }
// Use 9 to test all...
- int round = (i++ % 1);
+ int round = (i++ % 2);
long curr = System.currentTimeMillis();
String msg;
diff --git a/src/smile/java/org/codehaus/jackson/smile/SmileParser.java b/src/smile/java/org/codehaus/jackson/smile/SmileParser.java
index 53e2e10..b844aec 100644
--- a/src/smile/java/org/codehaus/jackson/smile/SmileParser.java
+++ b/src/smile/java/org/codehaus/jackson/smile/SmileParser.java
@@ -652,8 +652,9 @@
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int inPtr = _inputPtr;
_inputPtr += len;
+ final byte[] inBuf = _inputBuffer;
for (int end = inPtr + len; inPtr < end; ) {
- outBuf[outPtr++] = (char) _inputBuffer[inPtr++];
+ outBuf[outPtr++] = (char) inBuf[inPtr++];
}
_textBuffer.setCurrentLength(len);
return _textBuffer.contentsAsString();
@@ -674,25 +675,26 @@
int inPtr = _inputPtr;
_inputPtr += len;
final int[] codes = SmileConstants.sUtf8UnitLengths;
+ final byte[] inBuf = _inputBuffer;
for (int end = inPtr + len; inPtr < end; ) {
- int i = _inputBuffer[inPtr++] & 0xFF;
+ int i = inBuf[inPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
// trickiest one, need surrogate handling
switch (code) {
case 1:
- i = ((i & 0x1F) << 6) | (_inputBuffer[inPtr++] & 0x3F);
+ i = ((i & 0x1F) << 6) | (inBuf[inPtr++] & 0x3F);
break;
case 2:
i = ((i & 0x0F) << 12)
- | ((_inputBuffer[inPtr++] & 0x3F) << 6)
- | (_inputBuffer[inPtr++] & 0x3F);
+ | ((inBuf[inPtr++] & 0x3F) << 6)
+ | (inBuf[inPtr++] & 0x3F);
break;
case 3:
i = ((i & 0x07) << 18)
- | ((_inputBuffer[inPtr++] & 0x3F) << 12)
- | ((_inputBuffer[inPtr++] & 0x3F) << 6)
- | (_inputBuffer[inPtr++] & 0x3F);
+ | ((inBuf[inPtr++] & 0x3F) << 12)
+ | ((inBuf[inPtr++] & 0x3F) << 6)
+ | (inBuf[inPtr++] & 0x3F);
// note: this is the codepoint value; need to split, too
i -= 0x10000;
outBuf[outPtr++] = (char) (0xD800 | (i >> 10));
@@ -778,29 +780,29 @@
int inPtr = _inputPtr;
final byte[] inBuf = _inputBuffer;
do {
- int q = inBuf[inPtr++] << 8;
- q |= inBuf[inPtr++];
- q <<= 8;
- q |= inBuf[inPtr++];
- q <<= 8;
- q |= inBuf[inPtr++];
- _quadBuffer[offset++] = q;
+ int q = inBuf[inPtr++] << 8;
+ q |= inBuf[inPtr++];
+ q <<= 8;
+ q |= inBuf[inPtr++];
+ q <<= 8;
+ q |= inBuf[inPtr++];
+ _quadBuffer[offset++] = q;
} while ((len -= 4) > 3);
// and then leftovers
if (len > 0) {
- int q = inBuf[inPtr++];
- if (--len >= 0) {
- q = (q << 8) + inBuf[inPtr++];
- if (--len >= 0) {
- q = (q << 8) + inBuf[inPtr++];
- }
- }
- _quadBuffer[offset++] = q;
+ int q = inBuf[inPtr++];
+ if (--len >= 0) {
+ q = (q << 8) + inBuf[inPtr++];
+ if (--len >= 0) {
+ q = (q << 8) + inBuf[inPtr++];
+ }
+ }
+ _quadBuffer[offset++] = q;
}
return _symbols.findName(_quadBuffer, offset);
- }
+ }
- public static int[] _growArrayTo(int[] arr, int minSize)
+ private static int[] _growArrayTo(int[] arr, int minSize)
{
int[] newArray = new int[minSize + 4];
if (arr != null) {
@@ -867,9 +869,9 @@
case 4: // VInt (zigzag) or BigDecimal
int subtype = tb & 0x03;
if (subtype == 0) { // (v)int
- _finishInt();
+ _finishInt();
} else if (subtype == 1) { // (v)long
- _finishLong();
+ _finishLong();
} else if (subtype == 2) {
_finishBigInteger();
} else {
@@ -879,14 +881,14 @@
case 5: // other numbers
switch (tb & 0x03) {
case 0: // float
- _finishFloat();
- return;
+ _finishFloat();
+ return;
case 1: // double
- _finishDouble();
- return;
+ _finishDouble();
+ return;
case 2: // big-decimal
- _finishBigDecimal();
- return;
+ _finishBigDecimal();
+ return;
}
break;
}
@@ -910,36 +912,36 @@
if (value < 0) { // 6 bits
value &= 0x3F;
} else {
- if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
- }
- i = _inputBuffer[_inputPtr++];
- if (i >= 0) { // 13 bits
- value = (value << 7) + i;
- if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
- }
- i = _inputBuffer[_inputPtr++];
- if (i >= 0) {
- value = (value << 7) + i;
- if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
- }
- i = _inputBuffer[_inputPtr++];
- if (i >= 0) {
- value = (value << 7) + i;
- // and then we must get negative
- if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
- }
- i = _inputBuffer[_inputPtr++];
- if (i >= 0) {
- _reportError("Corrupt input; 32-bit VInt extends beyond 5 data bytes");
- }
- }
- }
- }
- value = (value << 6) + (i & 0x3F);
+ if (_inputPtr >= _inputEnd) {
+ loadMoreGuaranteed();
+ }
+ i = _inputBuffer[_inputPtr++];
+ if (i >= 0) { // 13 bits
+ value = (value << 7) + i;
+ if (_inputPtr >= _inputEnd) {
+ loadMoreGuaranteed();
+ }
+ i = _inputBuffer[_inputPtr++];
+ if (i >= 0) {
+ value = (value << 7) + i;
+ if (_inputPtr >= _inputEnd) {
+ loadMoreGuaranteed();
+ }
+ i = _inputBuffer[_inputPtr++];
+ if (i >= 0) {
+ value = (value << 7) + i;
+ // and then we must get negative
+ if (_inputPtr >= _inputEnd) {
+ loadMoreGuaranteed();
+ }
+ i = _inputBuffer[_inputPtr++];
+ if (i >= 0) {
+ _reportError("Corrupt input; 32-bit VInt extends beyond 5 data bytes");
+ }
+ }
+ }
+ }
+ value = (value << 6) + (i & 0x3F);
}
_numberInt = SmileUtil.zigzagDecode(value);
_numTypesValid = NR_INT;
@@ -952,17 +954,17 @@
long l = (long) _fourBytesToInt();
// and loop for the rest
while (true) {
- if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
- }
- int value = _inputBuffer[_inputPtr++];
- if (value < 0) {
- l = (l << 6) + (value & 0x3F);
- _numberLong = SmileUtil.zigzagDecode(l);
- _numTypesValid = NR_LONG;
- return;
- }
- l = (l << 7) + value;
+ if (_inputPtr >= _inputEnd) {
+ loadMoreGuaranteed();
+ }
+ int value = _inputBuffer[_inputPtr++];
+ if (value < 0) {
+ l = (l << 6) + (value & 0x3F);
+ _numberLong = SmileUtil.zigzagDecode(l);
+ _numTypesValid = NR_LONG;
+ return;
+ }
+ l = (l << 7) + value;
}
}
@@ -1118,14 +1120,12 @@
}
int outPtr = 0;
// Note: we count on fact that buffer must have at least 'len' (<= 64) empty char slots
- char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+ final char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int inPtr = _inputPtr;
_inputPtr += len;
-if (_inputPtr > _inputEnd) {
- throw new Error("Bad stuff; ptr now "+_inputPtr+"...");
-}
+ final byte[] inputBuf = _inputBuffer;
for (int end = inPtr + len; inPtr < end; ) {
- outBuf[outPtr++] = (char) _inputBuffer[inPtr++];
+ outBuf[outPtr++] = (char) inputBuf[inPtr++];
}
_textBuffer.setCurrentLength(len);
}
@@ -1141,32 +1141,33 @@
int inPtr = _inputPtr;
_inputPtr += len;
final int[] codes = SmileConstants.sUtf8UnitLengths;
+ final byte[] inputBuf = _inputBuffer;
for (int end = inPtr + len; inPtr < end; ) {
- int i = _inputBuffer[inPtr++] & 0xFF;
+ int i = inputBuf[inPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
// trickiest one, need surrogate handling
switch (code) {
case 1:
- i = ((i & 0x1F) << 6) | (_inputBuffer[inPtr++] & 0x3F);
+ i = ((i & 0x1F) << 6) | (inputBuf[inPtr++] & 0x3F);
break;
case 2:
i = ((i & 0x0F) << 12)
- | ((_inputBuffer[inPtr++] & 0x3F) << 6)
- | (_inputBuffer[inPtr++] & 0x3F);
- break;
+ | ((inputBuf[inPtr++] & 0x3F) << 6)
+ | (inputBuf[inPtr++] & 0x3F);
+ break;
case 3:
- i = ((i & 0x07) << 18)
- | ((_inputBuffer[inPtr++] & 0x3F) << 12)
- | ((_inputBuffer[inPtr++] & 0x3F) << 6)
- | (_inputBuffer[inPtr++] & 0x3F);
- // note: this is the codepoint value; need to split, too
- i -= 0x10000;
- outBuf[outPtr++] = (char) (0xD800 | (i >> 10));
- i = 0xDC00 | (i & 0x3FF);
- break;
+ i = ((i & 0x07) << 18)
+ | ((inputBuf[inPtr++] & 0x3F) << 12)
+ | ((inputBuf[inPtr++] & 0x3F) << 6)
+ | (inputBuf[inPtr++] & 0x3F);
+ // note: this is the codepoint value; need to split, too
+ i -= 0x10000;
+ outBuf[outPtr++] = (char) (0xD800 | (i >> 10));
+ i = 0xDC00 | (i & 0x3FF);
+ break;
default: // invalid
- _reportError("Invalid byte "+Integer.toHexString(i)+" in short Unicode text block");
+ _reportError("Invalid byte "+Integer.toHexString(i)+" in short Unicode text block");
}
}
outBuf[outPtr++] = (char) i;
@@ -1184,19 +1185,22 @@
if (_inputPtr >= _inputEnd) {
loadMoreGuaranteed();
}
- int left = _inputEnd - _inputPtr;
+ int inPtr = _inputPtr;
+ int left = _inputEnd - inPtr;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
left = Math.min(left, outBuf.length - outPtr);
do {
- byte b = _inputBuffer[_inputPtr++];
+ byte b = _inputBuffer[inPtr++];
if (b == SmileConstants.BYTE_MARKER_END_OF_STRING) {
+ _inputPtr = inPtr;
break main_loop;
}
outBuf[outPtr++] = (char) b;
} while (--left > 0);
+ _inputPtr = inPtr;
}
_textBuffer.setCurrentLength(outPtr);
}
@@ -1340,17 +1344,16 @@
* (note: can potentially skip invalid UTF-8 too)
*/
while (true) {
- final int end = _inputEnd;
- final byte[] buf = _inputBuffer;
- while (_inputPtr < end) {
- if (buf[_inputPtr++] == BYTE_MARKER_END_OF_STRING) {
- return;
- }
- }
- loadMoreGuaranteed();
+ final int end = _inputEnd;
+ final byte[] buf = _inputBuffer;
+ while (_inputPtr < end) {
+ if (buf[_inputPtr++] == BYTE_MARKER_END_OF_STRING) {
+ return;
+ }
+ }
+ loadMoreGuaranteed();
}
-
- //
+ // never gets here
case 2: // binary, raw
_skipBytes(_readUnsignedVInt());
return;