xref: /aosp_15_r20/external/jackson-databind/attic/ContainerBuilder.java (revision 0ed15c778abdfe0f5f51f6133673e1619d6e56e4)
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