Skip to content

Commit a3bd2f5

Browse files
committed
Remove mutability from BeanPropertyMap, to try to address #2027
1 parent eea460b commit a3bd2f5

File tree

1 file changed

+69
-42
lines changed

1 file changed

+69
-42
lines changed

src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class BeanPropertyMap
4343
* Number of entries stored in the hash area.
4444
*/
4545
private int _size;
46-
46+
4747
private int _spillCount;
4848

4949
/**
@@ -55,7 +55,7 @@ public class BeanPropertyMap
5555
* Array of properties in the exact order they were handed in. This is
5656
* used by as-array serialization, deserialization.
5757
*/
58-
private SettableBeanProperty[] _propsInOrder;
58+
private final SettableBeanProperty[] _propsInOrder;
5959

6060
/**
6161
* Configuration of alias mappings, indexed by unmodified property name
@@ -75,7 +75,7 @@ public class BeanPropertyMap
7575
* @since 2.9
7676
*/
7777
private final Map<String,String> _aliasMapping;
78-
78+
7979
/**
8080
* @since 2.9
8181
*/
@@ -89,6 +89,70 @@ public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty>
8989
init(props);
9090
}
9191

92+
/* Copy constructors used when a property can replace existing one
93+
*
94+
* @since 2.9.6
95+
*/
96+
private BeanPropertyMap(BeanPropertyMap src,
97+
SettableBeanProperty newProp, int hashIndex, int orderedIndex)
98+
{
99+
// First, copy most fields as is:
100+
_caseInsensitive = src._caseInsensitive;
101+
_hashMask = src._hashMask;
102+
_size = src._size;
103+
_spillCount = src._spillCount;
104+
_aliasDefs = src._aliasDefs;
105+
_aliasMapping = src._aliasMapping;
106+
107+
// but then make deep copy of arrays to modify
108+
_hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
109+
_propsInOrder = Arrays.copyOf(src._propsInOrder, src._propsInOrder.length);
110+
_hashArea[hashIndex] = newProp;
111+
_propsInOrder[orderedIndex] = newProp;
112+
}
113+
114+
/* Copy constructors used when a property needs to be appended (can't replace)
115+
*
116+
* @since 2.9.6
117+
*/
118+
private BeanPropertyMap(BeanPropertyMap src,
119+
SettableBeanProperty newProp, String key, int slot)
120+
{
121+
// First, copy most fields as is:
122+
_caseInsensitive = src._caseInsensitive;
123+
_hashMask = src._hashMask;
124+
_size = src._size;
125+
_spillCount = src._spillCount;
126+
_aliasDefs = src._aliasDefs;
127+
_aliasMapping = src._aliasMapping;
128+
129+
// but then make deep copy of arrays to modify
130+
_hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
131+
int last = src._propsInOrder.length;
132+
// and append property at the end of ordering
133+
_propsInOrder = Arrays.copyOf(src._propsInOrder, last+1);
134+
_propsInOrder[last] = newProp;
135+
136+
final int hashSize = _hashMask+1;
137+
int ix = (slot<<1);
138+
139+
// primary slot not free?
140+
if (_hashArea[ix] != null) {
141+
// secondary?
142+
ix = (hashSize + (slot >> 1)) << 1;
143+
if (_hashArea[ix] != null) {
144+
// ok, spill over.
145+
ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount;
146+
_spillCount += 2;
147+
if (ix >= _hashArea.length) {
148+
_hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4);
149+
}
150+
}
151+
}
152+
_hashArea[ix] = key;
153+
_hashArea[ix+1] = newProp;
154+
}
155+
92156
@Deprecated // since 2.8
93157
public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
94158
{
@@ -159,15 +223,11 @@ protected void init(Collection<SettableBeanProperty> props)
159223
}
160224
}
161225
}
162-
//System.err.println(" add '"+key+" at #"+(ix>>1)+"/"+size+" (hashed at "+slot+")");
163226
hashed[ix] = key;
164227
hashed[ix+1] = prop;
165228

166229
// and aliases
167230
}
168-
//for (int i = 0; i < hashed.length; i += 2) {
169-
//System.err.printf("#%02d: %s\n", i>>1, (hashed[i] == null) ? "-" : hashed[i]);
170-
//}
171231
_hashArea = hashed;
172232
_spillCount = spillCount;
173233
}
@@ -217,46 +277,13 @@ public BeanPropertyMap withProperty(SettableBeanProperty newProp)
217277
for (int i = 1, end = _hashArea.length; i < end; i += 2) {
218278
SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
219279
if ((prop != null) && prop.getName().equals(key)) {
220-
_hashArea[i] = newProp;
221-
_propsInOrder[_findFromOrdered(prop)] = newProp;
222-
return this;
280+
return new BeanPropertyMap(this, newProp, i, _findFromOrdered(prop));
223281
}
224282
}
225283
// If not, append
226284
final int slot = _hashCode(key);
227-
final int hashSize = _hashMask+1;
228-
int ix = (slot<<1);
229-
230-
// primary slot not free?
231-
if (_hashArea[ix] != null) {
232-
// secondary?
233-
ix = (hashSize + (slot >> 1)) << 1;
234-
if (_hashArea[ix] != null) {
235-
// ok, spill over.
236-
ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount;
237-
_spillCount += 2;
238-
if (ix >= _hashArea.length) {
239-
_hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4);
240-
// Uncomment for debugging only
241-
//for (int i = 0; i < _hashArea.length; i += 2) {
242-
// if (_hashArea[i] != null) {
243-
// System.err.println("Property #"+(i/2)+" '"+_hashArea[i]+"'...");
244-
// }
245-
//}
246-
//System.err.println("And new propr #"+slot+" '"+key+"'");
247-
}
248-
}
249-
}
250-
_hashArea[ix] = key;
251-
_hashArea[ix+1] = newProp;
252-
253-
int last = _propsInOrder.length;
254-
_propsInOrder = Arrays.copyOf(_propsInOrder, last+1);
255-
_propsInOrder[last] = newProp;
256285

257-
// should we just create a new one? Or is resetting ok?
258-
259-
return this;
286+
return new BeanPropertyMap(this, newProp, key, slot);
260287
}
261288

262289
public BeanPropertyMap assignIndexes()

0 commit comments

Comments
 (0)