1*0ed15c77SAndroid Build Coastguard Worker package com.fasterxml.jackson.databind.util; 2*0ed15c77SAndroid Build Coastguard Worker 3*0ed15c77SAndroid Build Coastguard Worker import java.lang.reflect.Array; 4*0ed15c77SAndroid Build Coastguard Worker import java.util.*; 5*0ed15c77SAndroid Build Coastguard Worker 6*0ed15c77SAndroid Build Coastguard Worker /** 7*0ed15c77SAndroid Build Coastguard Worker * Helper class used for constructing "untyped" {@link java.util.List}, 8*0ed15c77SAndroid Build Coastguard Worker * {@link java.util.Map} and <code>Object[]</code> values. 9*0ed15c77SAndroid Build Coastguard Worker * Could help performance if a single instance can be used for building 10*0ed15c77SAndroid Build Coastguard Worker * nested Maps, Lists/Object[] of relatively small size. 11*0ed15c77SAndroid Build Coastguard Worker * Whether use makes sense depends; currently this class is not used. 12*0ed15c77SAndroid Build Coastguard Worker */ 13*0ed15c77SAndroid Build Coastguard Worker public final class ContainerBuilder 14*0ed15c77SAndroid Build Coastguard Worker { 15*0ed15c77SAndroid Build Coastguard Worker private final static int MAX_BUF = 1000; 16*0ed15c77SAndroid Build Coastguard Worker 17*0ed15c77SAndroid Build Coastguard Worker /** 18*0ed15c77SAndroid Build Coastguard Worker * Buffer in which contents are being buffered (except for cases where 19*0ed15c77SAndroid Build Coastguard Worker * size has grown too big to bother with separate buffer) 20*0ed15c77SAndroid Build Coastguard Worker */ 21*0ed15c77SAndroid Build Coastguard Worker private Object[] b; 22*0ed15c77SAndroid Build Coastguard Worker 23*0ed15c77SAndroid Build Coastguard Worker /** 24*0ed15c77SAndroid Build Coastguard Worker * Pointer to the next available slot in temporary buffer. 25*0ed15c77SAndroid Build Coastguard Worker */ 26*0ed15c77SAndroid Build Coastguard Worker private int tail; 27*0ed15c77SAndroid Build Coastguard Worker 28*0ed15c77SAndroid Build Coastguard Worker /** 29*0ed15c77SAndroid Build Coastguard Worker * When building potentially multiple containers, we need to keep track of 30*0ed15c77SAndroid Build Coastguard Worker * the starting pointer for the current container. 31*0ed15c77SAndroid Build Coastguard Worker */ 32*0ed15c77SAndroid Build Coastguard Worker private int start; 33*0ed15c77SAndroid Build Coastguard Worker 34*0ed15c77SAndroid Build Coastguard Worker /** 35*0ed15c77SAndroid Build Coastguard Worker * In cases where size of buffered contents has grown big enough that buffering 36*0ed15c77SAndroid Build Coastguard Worker * does not make sense, an actual {@link java.util.List} will be constructed 37*0ed15c77SAndroid Build Coastguard Worker * earlier and used instead of buffering. 38*0ed15c77SAndroid Build Coastguard Worker */ 39*0ed15c77SAndroid Build Coastguard Worker private List<Object> list; 40*0ed15c77SAndroid Build Coastguard Worker 41*0ed15c77SAndroid Build Coastguard Worker /** 42*0ed15c77SAndroid Build Coastguard Worker * Similar to <code>list</code>, we may sometimes eagerly construct result 43*0ed15c77SAndroid Build Coastguard Worker * {@link java.util.Map} and skip actual buffering. 44*0ed15c77SAndroid Build Coastguard Worker */ 45*0ed15c77SAndroid Build Coastguard Worker private Map<String,Object> map; 46*0ed15c77SAndroid Build Coastguard Worker ContainerBuilder(int bufSize)47*0ed15c77SAndroid Build Coastguard Worker public ContainerBuilder(int bufSize) { 48*0ed15c77SAndroid Build Coastguard Worker b = new Object[bufSize & ~1]; 49*0ed15c77SAndroid Build Coastguard Worker } 50*0ed15c77SAndroid Build Coastguard Worker canReuse()51*0ed15c77SAndroid Build Coastguard Worker public boolean canReuse() { 52*0ed15c77SAndroid Build Coastguard Worker return (list == null) && (map == null); 53*0ed15c77SAndroid Build Coastguard Worker } 54*0ed15c77SAndroid Build Coastguard Worker bufferLength()55*0ed15c77SAndroid Build Coastguard Worker public int bufferLength() { 56*0ed15c77SAndroid Build Coastguard Worker return b.length; 57*0ed15c77SAndroid Build Coastguard Worker } 58*0ed15c77SAndroid Build Coastguard Worker 59*0ed15c77SAndroid Build Coastguard Worker /* 60*0ed15c77SAndroid Build Coastguard Worker /********************************************************** 61*0ed15c77SAndroid Build Coastguard Worker /* Public API 62*0ed15c77SAndroid Build Coastguard Worker /********************************************************** 63*0ed15c77SAndroid Build Coastguard Worker */ 64*0ed15c77SAndroid Build Coastguard Worker start()65*0ed15c77SAndroid Build Coastguard Worker public int start() { 66*0ed15c77SAndroid Build Coastguard Worker if (list != null || map != null) { 67*0ed15c77SAndroid Build Coastguard Worker throw new IllegalStateException(); 68*0ed15c77SAndroid Build Coastguard Worker } 69*0ed15c77SAndroid Build Coastguard Worker final int prevStart = start; 70*0ed15c77SAndroid Build Coastguard Worker start = tail; 71*0ed15c77SAndroid Build Coastguard Worker return prevStart; 72*0ed15c77SAndroid Build Coastguard Worker } 73*0ed15c77SAndroid Build Coastguard Worker startList(Object value)74*0ed15c77SAndroid Build Coastguard Worker public int startList(Object value) { 75*0ed15c77SAndroid Build Coastguard Worker if (list != null || map != null) { 76*0ed15c77SAndroid Build Coastguard Worker throw new IllegalStateException(); 77*0ed15c77SAndroid Build Coastguard Worker } 78*0ed15c77SAndroid Build Coastguard Worker final int prevStart = start; 79*0ed15c77SAndroid Build Coastguard Worker start = tail; 80*0ed15c77SAndroid Build Coastguard Worker add(value); 81*0ed15c77SAndroid Build Coastguard Worker return prevStart; 82*0ed15c77SAndroid Build Coastguard Worker } 83*0ed15c77SAndroid Build Coastguard Worker startMap(String key, Object value)84*0ed15c77SAndroid Build Coastguard Worker public int startMap(String key, Object value) { 85*0ed15c77SAndroid Build Coastguard Worker if (list != null || map != null) { 86*0ed15c77SAndroid Build Coastguard Worker throw new IllegalStateException(); 87*0ed15c77SAndroid Build Coastguard Worker } 88*0ed15c77SAndroid Build Coastguard Worker final int prevStart = start; 89*0ed15c77SAndroid Build Coastguard Worker start = tail; 90*0ed15c77SAndroid Build Coastguard Worker put(key, value); 91*0ed15c77SAndroid Build Coastguard Worker return prevStart; 92*0ed15c77SAndroid Build Coastguard Worker } 93*0ed15c77SAndroid Build Coastguard Worker add(Object value)94*0ed15c77SAndroid Build Coastguard Worker public void add(Object value) { 95*0ed15c77SAndroid Build Coastguard Worker if (list != null) { 96*0ed15c77SAndroid Build Coastguard Worker list.add(value); 97*0ed15c77SAndroid Build Coastguard Worker } else if (tail >= b.length) { 98*0ed15c77SAndroid Build Coastguard Worker _expandList(value); 99*0ed15c77SAndroid Build Coastguard Worker } else { 100*0ed15c77SAndroid Build Coastguard Worker b[tail++] = value; 101*0ed15c77SAndroid Build Coastguard Worker } 102*0ed15c77SAndroid Build Coastguard Worker } 103*0ed15c77SAndroid Build Coastguard Worker put(String key, Object value)104*0ed15c77SAndroid Build Coastguard Worker public void put(String key, Object value) { 105*0ed15c77SAndroid Build Coastguard Worker if (map != null) { 106*0ed15c77SAndroid Build Coastguard Worker map.put(key, value); 107*0ed15c77SAndroid Build Coastguard Worker } else if ((tail + 2) > b.length) { 108*0ed15c77SAndroid Build Coastguard Worker _expandMap(key, value); 109*0ed15c77SAndroid Build Coastguard Worker } else { 110*0ed15c77SAndroid Build Coastguard Worker b[tail++] = key; 111*0ed15c77SAndroid Build Coastguard Worker b[tail++] = value; 112*0ed15c77SAndroid Build Coastguard Worker } 113*0ed15c77SAndroid Build Coastguard Worker } 114*0ed15c77SAndroid Build Coastguard Worker finishList(int prevStart)115*0ed15c77SAndroid Build Coastguard Worker public List<Object> finishList(int prevStart) 116*0ed15c77SAndroid Build Coastguard Worker { 117*0ed15c77SAndroid Build Coastguard Worker List<Object> l = list; 118*0ed15c77SAndroid Build Coastguard Worker if (l == null) { 119*0ed15c77SAndroid Build Coastguard Worker l = _buildList(true); 120*0ed15c77SAndroid Build Coastguard Worker } else { 121*0ed15c77SAndroid Build Coastguard Worker list = null; 122*0ed15c77SAndroid Build Coastguard Worker } 123*0ed15c77SAndroid Build Coastguard Worker start = prevStart; 124*0ed15c77SAndroid Build Coastguard Worker return l; 125*0ed15c77SAndroid Build Coastguard Worker } 126*0ed15c77SAndroid Build Coastguard Worker finishArray(int prevStart)127*0ed15c77SAndroid Build Coastguard Worker public Object[] finishArray(int prevStart) 128*0ed15c77SAndroid Build Coastguard Worker { 129*0ed15c77SAndroid Build Coastguard Worker Object[] result; 130*0ed15c77SAndroid Build Coastguard Worker if (list == null) { 131*0ed15c77SAndroid Build Coastguard Worker result = Arrays.copyOfRange(b, start, tail); 132*0ed15c77SAndroid Build Coastguard Worker } else { 133*0ed15c77SAndroid Build Coastguard Worker result = list.toArray(new Object[tail - start]); 134*0ed15c77SAndroid Build Coastguard Worker list = null; 135*0ed15c77SAndroid Build Coastguard Worker } 136*0ed15c77SAndroid Build Coastguard Worker start = prevStart; 137*0ed15c77SAndroid Build Coastguard Worker return result; 138*0ed15c77SAndroid Build Coastguard Worker } 139*0ed15c77SAndroid Build Coastguard Worker finishArray(int prevStart, Class<T> elemType)140*0ed15c77SAndroid Build Coastguard Worker public <T> Object[] finishArray(int prevStart, Class<T> elemType) 141*0ed15c77SAndroid Build Coastguard Worker { 142*0ed15c77SAndroid Build Coastguard Worker final int size = tail-start; 143*0ed15c77SAndroid Build Coastguard Worker @SuppressWarnings("unchecked") 144*0ed15c77SAndroid Build Coastguard Worker T[] result = (T[]) Array.newInstance(elemType, size); 145*0ed15c77SAndroid Build Coastguard Worker 146*0ed15c77SAndroid Build Coastguard Worker if (list == null) { 147*0ed15c77SAndroid Build Coastguard Worker System.arraycopy(b, start, result, 0, size); 148*0ed15c77SAndroid Build Coastguard Worker } else { 149*0ed15c77SAndroid Build Coastguard Worker result = list.toArray(result); 150*0ed15c77SAndroid Build Coastguard Worker list = null; 151*0ed15c77SAndroid Build Coastguard Worker } 152*0ed15c77SAndroid Build Coastguard Worker start = prevStart; 153*0ed15c77SAndroid Build Coastguard Worker return result; 154*0ed15c77SAndroid Build Coastguard Worker } 155*0ed15c77SAndroid Build Coastguard Worker finishMap(int prevStart)156*0ed15c77SAndroid Build Coastguard Worker public Map<String,Object> finishMap(int prevStart) 157*0ed15c77SAndroid Build Coastguard Worker { 158*0ed15c77SAndroid Build Coastguard Worker Map<String,Object> m = map; 159*0ed15c77SAndroid Build Coastguard Worker 160*0ed15c77SAndroid Build Coastguard Worker if (m == null) { 161*0ed15c77SAndroid Build Coastguard Worker m = _buildMap(true); 162*0ed15c77SAndroid Build Coastguard Worker } else { 163*0ed15c77SAndroid Build Coastguard Worker map = null; 164*0ed15c77SAndroid Build Coastguard Worker } 165*0ed15c77SAndroid Build Coastguard Worker start = prevStart; 166*0ed15c77SAndroid Build Coastguard Worker return m; 167*0ed15c77SAndroid Build Coastguard Worker } 168*0ed15c77SAndroid Build Coastguard Worker 169*0ed15c77SAndroid Build Coastguard Worker /* 170*0ed15c77SAndroid Build Coastguard Worker /********************************************************** 171*0ed15c77SAndroid Build Coastguard Worker /* Internal methods 172*0ed15c77SAndroid Build Coastguard Worker /********************************************************** 173*0ed15c77SAndroid Build Coastguard Worker */ 174*0ed15c77SAndroid Build Coastguard Worker _expandList(Object value)175*0ed15c77SAndroid Build Coastguard Worker private void _expandList(Object value) { 176*0ed15c77SAndroid Build Coastguard Worker if (b.length < MAX_BUF) { // can still expand 177*0ed15c77SAndroid Build Coastguard Worker b = Arrays.copyOf(b, b.length << 1); 178*0ed15c77SAndroid Build Coastguard Worker b[tail++] = value; 179*0ed15c77SAndroid Build Coastguard Worker } else { 180*0ed15c77SAndroid Build Coastguard Worker list = _buildList(false); 181*0ed15c77SAndroid Build Coastguard Worker list.add(value); 182*0ed15c77SAndroid Build Coastguard Worker } 183*0ed15c77SAndroid Build Coastguard Worker } 184*0ed15c77SAndroid Build Coastguard Worker _buildList(boolean isComplete)185*0ed15c77SAndroid Build Coastguard Worker private List<Object> _buildList(boolean isComplete) 186*0ed15c77SAndroid Build Coastguard Worker { 187*0ed15c77SAndroid Build Coastguard Worker int currLen = tail - start; 188*0ed15c77SAndroid Build Coastguard Worker if (isComplete) { 189*0ed15c77SAndroid Build Coastguard Worker if (currLen < 2) { 190*0ed15c77SAndroid Build Coastguard Worker currLen = 2; 191*0ed15c77SAndroid Build Coastguard Worker } 192*0ed15c77SAndroid Build Coastguard Worker } else { 193*0ed15c77SAndroid Build Coastguard Worker if (currLen < 20) { 194*0ed15c77SAndroid Build Coastguard Worker currLen = 20; 195*0ed15c77SAndroid Build Coastguard Worker } else if (currLen < MAX_BUF) { 196*0ed15c77SAndroid Build Coastguard Worker currLen += (currLen>>1); 197*0ed15c77SAndroid Build Coastguard Worker } else { 198*0ed15c77SAndroid Build Coastguard Worker currLen += (currLen>>2); 199*0ed15c77SAndroid Build Coastguard Worker } 200*0ed15c77SAndroid Build Coastguard Worker } 201*0ed15c77SAndroid Build Coastguard Worker List<Object> l = new ArrayList<Object>(currLen); 202*0ed15c77SAndroid Build Coastguard Worker for (int i = start; i < tail; ++i) { 203*0ed15c77SAndroid Build Coastguard Worker l.add(b[i]); 204*0ed15c77SAndroid Build Coastguard Worker } 205*0ed15c77SAndroid Build Coastguard Worker tail = start; // reset buffered entries 206*0ed15c77SAndroid Build Coastguard Worker return l; 207*0ed15c77SAndroid Build Coastguard Worker } 208*0ed15c77SAndroid Build Coastguard Worker _expandMap(String key, Object value)209*0ed15c77SAndroid Build Coastguard Worker private void _expandMap(String key, Object value) { 210*0ed15c77SAndroid Build Coastguard Worker if (b.length < MAX_BUF) { // can still expand 211*0ed15c77SAndroid Build Coastguard Worker b = Arrays.copyOf(b, b.length << 1); 212*0ed15c77SAndroid Build Coastguard Worker b[tail++] = key; 213*0ed15c77SAndroid Build Coastguard Worker b[tail++] = value; 214*0ed15c77SAndroid Build Coastguard Worker } else { 215*0ed15c77SAndroid Build Coastguard Worker map = _buildMap(false); 216*0ed15c77SAndroid Build Coastguard Worker map.put(key, value); 217*0ed15c77SAndroid Build Coastguard Worker } 218*0ed15c77SAndroid Build Coastguard Worker } 219*0ed15c77SAndroid Build Coastguard Worker _buildMap(boolean isComplete)220*0ed15c77SAndroid Build Coastguard Worker private Map<String,Object> _buildMap(boolean isComplete) 221*0ed15c77SAndroid Build Coastguard Worker { 222*0ed15c77SAndroid Build Coastguard Worker int size = (tail - start) >> 1; 223*0ed15c77SAndroid Build Coastguard Worker if (isComplete) { // when complete, optimize to smallest size 224*0ed15c77SAndroid Build Coastguard Worker if (size <= 3) { // 3 or fewer entries, hash table of 4 225*0ed15c77SAndroid Build Coastguard Worker size = 4; 226*0ed15c77SAndroid Build Coastguard Worker } else if (size <= 40) { 227*0ed15c77SAndroid Build Coastguard Worker size += (size>>1); 228*0ed15c77SAndroid Build Coastguard Worker } else { 229*0ed15c77SAndroid Build Coastguard Worker size += (size>>2) + (size>>4); // * 1.3125 230*0ed15c77SAndroid Build Coastguard Worker } 231*0ed15c77SAndroid Build Coastguard Worker } else { 232*0ed15c77SAndroid Build Coastguard Worker if (size < 10) { 233*0ed15c77SAndroid Build Coastguard Worker size = 16; 234*0ed15c77SAndroid Build Coastguard Worker } else if (size < MAX_BUF) { 235*0ed15c77SAndroid Build Coastguard Worker size += (size>>1); 236*0ed15c77SAndroid Build Coastguard Worker } else { 237*0ed15c77SAndroid Build Coastguard Worker size += (size/3); 238*0ed15c77SAndroid Build Coastguard Worker } 239*0ed15c77SAndroid Build Coastguard Worker } 240*0ed15c77SAndroid Build Coastguard Worker Map<String,Object> m = new LinkedHashMap<String,Object>(size, 0.8f); 241*0ed15c77SAndroid Build Coastguard Worker for (int i = start; i < tail; i += 2) { 242*0ed15c77SAndroid Build Coastguard Worker m.put((String) b[i], b[i+1]); 243*0ed15c77SAndroid Build Coastguard Worker } 244*0ed15c77SAndroid Build Coastguard Worker tail = start; // reset buffered entries 245*0ed15c77SAndroid Build Coastguard Worker return m; 246*0ed15c77SAndroid Build Coastguard Worker } 247*0ed15c77SAndroid Build Coastguard Worker } 248