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;