package com.fasterxml.jackson.databind.util; import java.lang.reflect.Array; import java.util.*; /** * Helper class used for constructing "untyped" {@link java.util.List}, * {@link java.util.Map} and <code>Object[]</code> values. * Could help performance if a single instance can be used for building * nested Maps, Lists/Object[] of relatively small size. * Whether use makes sense depends; currently this class is not used. */ public final class ContainerBuilder { private final static int MAX_BUF = 1000; /** * Buffer in which contents are being buffered (except for cases where * size has grown too big to bother with separate buffer) */ private Object[] b; /** * Pointer to the next available slot in temporary buffer. */ private int tail; /** * When building potentially multiple containers, we need to keep track of * the starting pointer for the current container. */ private int start; /** * In cases where size of buffered contents has grown big enough that buffering * does not make sense, an actual {@link java.util.List} will be constructed * earlier and used instead of buffering. */ private List<Object> list; /** * Similar to <code>list</code>, we may sometimes eagerly construct result * {@link java.util.Map} and skip actual buffering. */ private Map<String,Object> map; public ContainerBuilder(int bufSize) { b = new Object[bufSize & ~1]; } public boolean canReuse() { return (list == null) && (map == null); } public int bufferLength() { return b.length; } /* /********************************************************** /* Public API /********************************************************** */ public int start() { if (list != null || map != null) { throw new IllegalStateException(); } final int prevStart = start; start = tail; return prevStart; } public int startList(Object value) { if (list != null || map != null) { throw new IllegalStateException(); } final int prevStart = start; start = tail; add(value); return prevStart; } public int startMap(String key, Object value) { if (list != null || map != null) { throw new IllegalStateException(); } final int prevStart = start; start = tail; put(key, value); return prevStart; } public void add(Object value) { if (list != null) { list.add(value); } else if (tail >= b.length) { _expandList(value); } else { b[tail++] = value; } } public void put(String key, Object value) { if (map != null) { map.put(key, value); } else if ((tail + 2) > b.length) { _expandMap(key, value); } else { b[tail++] = key; b[tail++] = value; } } public List<Object> finishList(int prevStart) { List<Object> l = list; if (l == null) { l = _buildList(true); } else { list = null; } start = prevStart; return l; } public Object[] finishArray(int prevStart) { Object[] result; if (list == null) { result = Arrays.copyOfRange(b, start, tail); } else { result = list.toArray(new Object[tail - start]); list = null; } start = prevStart; return result; } public <T> Object[] finishArray(int prevStart, Class<T> elemType) { final int size = tail-start; @SuppressWarnings("unchecked") T[] result = (T[]) Array.newInstance(elemType, size); if (list == null) { System.arraycopy(b, start, result, 0, size); } else { result = list.toArray(result); list = null; } start = prevStart; return result; } public Map<String,Object> finishMap(int prevStart) { Map<String,Object> m = map; if (m == null) { m = _buildMap(true); } else { map = null; } start = prevStart; return m; } /* /********************************************************** /* Internal methods /********************************************************** */ private void _expandList(Object value) { if (b.length < MAX_BUF) { // can still expand b = Arrays.copyOf(b, b.length << 1); b[tail++] = value; } else { list = _buildList(false); list.add(value); } } private List<Object> _buildList(boolean isComplete) { int currLen = tail - start; if (isComplete) { if (currLen < 2) { currLen = 2; } } else { if (currLen < 20) { currLen = 20; } else if (currLen < MAX_BUF) { currLen += (currLen>>1); } else { currLen += (currLen>>2); } } List<Object> l = new ArrayList<Object>(currLen); for (int i = start; i < tail; ++i) { l.add(b[i]); } tail = start; // reset buffered entries return l; } private void _expandMap(String key, Object value) { if (b.length < MAX_BUF) { // can still expand b = Arrays.copyOf(b, b.length << 1); b[tail++] = key; b[tail++] = value; } else { map = _buildMap(false); map.put(key, value); } } private Map<String,Object> _buildMap(boolean isComplete) { int size = (tail - start) >> 1; if (isComplete) { // when complete, optimize to smallest size if (size <= 3) { // 3 or fewer entries, hash table of 4 size = 4; } else if (size <= 40) { size += (size>>1); } else { size += (size>>2) + (size>>4); // * 1.3125 } } else { if (size < 10) { size = 16; } else if (size < MAX_BUF) { size += (size>>1); } else { size += (size/3); } } Map<String,Object> m = new LinkedHashMap<String,Object>(size, 0.8f); for (int i = start; i < tail; i += 2) { m.put((String) b[i], b[i+1]); } tail = start; // reset buffered entries return m; } }