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