diff options
Diffstat (limited to 'store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java')
-rw-r--r-- | store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java | 255 |
1 files changed, 127 insertions, 128 deletions
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java index 04a9b19a..b507763f 100644 --- a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java +++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/ImmutableNode.java | |||
@@ -15,15 +15,16 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
15 | */ | 15 | */ |
16 | final int nodeMap; | 16 | final int nodeMap; |
17 | /** | 17 | /** |
18 | * Stores Keys, Values, and subnodes. Structure: (K,V)*,NODE; NODES are stored backwards. | 18 | * Stores Keys, Values, and subnodes. Structure: (K,V)*,NODE; NODES are stored |
19 | * backwards. | ||
19 | */ | 20 | */ |
20 | final Object[] content; | 21 | final Object[] content; |
21 | 22 | ||
22 | /** | 23 | /** |
23 | * Hash code derived from immutable hash code | 24 | * Hash code derived from immutable hash code |
24 | */ | 25 | */ |
25 | final int precalculatedHash; | 26 | final int precalculatedHash; |
26 | 27 | ||
27 | private ImmutableNode(int dataMap, int nodeMap, Object[] content, int precalculatedHash) { | 28 | private ImmutableNode(int dataMap, int nodeMap, Object[] content, int precalculatedHash) { |
28 | super(); | 29 | super(); |
29 | this.dataMap = dataMap; | 30 | this.dataMap = dataMap; |
@@ -41,83 +42,87 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
41 | * available. | 42 | * available. |
42 | * @return an immutable version of the input node. | 43 | * @return an immutable version of the input node. |
43 | */ | 44 | */ |
44 | @SuppressWarnings("unchecked") | 45 | static <K, V> ImmutableNode<K, V> constructImmutable(MutableNode<K, V> node, |
45 | static <K,V> ImmutableNode<K,V> constructImmutable(MutableNode<K,V> node, Map<Node<K, V>, ImmutableNode<K, V>> cache) { | 46 | Map<Node<K, V>, ImmutableNode<K, V>> cache) { |
46 | // 1. try to return from cache | 47 | // 1. try to return from cache |
47 | if(cache != null) { | 48 | if (cache != null) { |
48 | ImmutableNode<K, V> cachedResult = cache.get(node); | 49 | ImmutableNode<K, V> cachedResult = cache.get(node); |
49 | if(cachedResult != null) { | 50 | if (cachedResult != null) { |
50 | // 1.1 Already cached, return from cache. | 51 | // 1.1 Already cached, return from cache. |
51 | return cachedResult; | 52 | return cachedResult; |
52 | } | 53 | } |
53 | } | 54 | } |
54 | 55 | ||
55 | // 2. otherwise construct a new ImmutableNode | 56 | // 2. otherwise construct a new ImmutableNode |
56 | int size = 0; | 57 | int size = 0; |
57 | for(int i = 0; i<node.content.length; i++) { | 58 | for (int i = 0; i < node.content.length; i++) { |
58 | if(node.content[i]!=null) { | 59 | if (node.content[i] != null) { |
59 | size++; | 60 | size++; |
60 | } | 61 | } |
61 | } | 62 | } |
62 | 63 | ||
63 | int datas = 0; | 64 | int datas = 0; |
64 | int nodes = 0; | 65 | int nodes = 0; |
65 | int resultDataMap = 0; | 66 | int resultDataMap = 0; |
66 | int resultNodeMap = 0; | 67 | int resultNodeMap = 0; |
67 | final Object[] resultContent = new Object[size]; | 68 | final Object[] resultContent = new Object[size]; |
68 | int bitposition = 1; | 69 | int bitposition = 1; |
69 | for(int i = 0; i<FACTOR; i++) { | 70 | for (int i = 0; i < FACTOR; i++) { |
70 | Object key = node.content[i*2]; | 71 | Object key = node.content[i * 2]; |
71 | if(key != null) { | 72 | if (key != null) { |
72 | resultDataMap |= bitposition; | 73 | resultDataMap |= bitposition; |
73 | resultContent[datas*2] = key; | 74 | resultContent[datas * 2] = key; |
74 | resultContent[datas*2+1] = node.content[i*2+1]; | 75 | resultContent[datas * 2 + 1] = node.content[i * 2 + 1]; |
75 | datas++; | 76 | datas++; |
76 | } else { | 77 | } else { |
77 | Node<K,V> subnode = (Node<K, V>) node.content[i*2+1]; | 78 | @SuppressWarnings("unchecked") |
78 | if(subnode != null) { | 79 | var subnode = (Node<K, V>) node.content[i * 2 + 1]; |
80 | if (subnode != null) { | ||
79 | ImmutableNode<K, V> immutableSubnode = subnode.toImmutable(cache); | 81 | ImmutableNode<K, V> immutableSubnode = subnode.toImmutable(cache); |
80 | resultNodeMap |=bitposition; | 82 | resultNodeMap |= bitposition; |
81 | resultContent[size-1-nodes] = immutableSubnode; | 83 | resultContent[size - 1 - nodes] = immutableSubnode; |
82 | nodes++; | 84 | nodes++; |
83 | } | 85 | } |
84 | } | 86 | } |
85 | bitposition<<=1; | 87 | bitposition <<= 1; |
86 | } | 88 | } |
87 | final int resultHash = node.hashCode(); | 89 | final int resultHash = node.hashCode(); |
88 | ImmutableNode<K,V> newImmutable = new ImmutableNode<>(resultDataMap, resultNodeMap, resultContent, resultHash); | 90 | var newImmutable = new ImmutableNode<K, V>(resultDataMap, resultNodeMap, resultContent, resultHash); |
89 | 91 | ||
90 | // 3. save new immutable. | 92 | // 3. save new immutable. |
91 | if(cache != null) { | 93 | if (cache != null) { |
92 | cache.put(newImmutable, newImmutable); | 94 | cache.put(newImmutable, newImmutable); |
93 | } | 95 | } |
94 | return newImmutable; | 96 | return newImmutable; |
95 | } | 97 | } |
96 | 98 | ||
97 | private int index(int bitmap, int bitpos) { | 99 | private int index(int bitmap, int bitpos) { |
98 | return Integer.bitCount(bitmap & (bitpos-1)); | 100 | return Integer.bitCount(bitmap & (bitpos - 1)); |
99 | } | 101 | } |
100 | 102 | ||
101 | @SuppressWarnings("unchecked") | ||
102 | @Override | 103 | @Override |
103 | public V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) { | 104 | public V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) { |
104 | int selectedHashFragment = hashFragment(hash,shiftDepth(depth)); | 105 | int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); |
105 | int bitposition = 1 << selectedHashFragment; | 106 | int bitposition = 1 << selectedHashFragment; |
106 | // If the key is stored as a data | 107 | // If the key is stored as a data |
107 | if((dataMap & bitposition) != 0) { | 108 | if ((dataMap & bitposition) != 0) { |
108 | int keyIndex = 2*index(dataMap, bitposition); | 109 | int keyIndex = 2 * index(dataMap, bitposition); |
110 | @SuppressWarnings("unchecked") | ||
109 | K keyCandidate = (K) content[keyIndex]; | 111 | K keyCandidate = (K) content[keyIndex]; |
110 | if(keyCandidate.equals(key)) { | 112 | if (keyCandidate.equals(key)) { |
111 | return (V) content[keyIndex+1]; | 113 | @SuppressWarnings("unchecked") |
114 | V value = (V) content[keyIndex + 1]; | ||
115 | return value; | ||
112 | } else { | 116 | } else { |
113 | return defaultValue; | 117 | return defaultValue; |
114 | } | 118 | } |
115 | } | 119 | } |
116 | // the key is stored as a node | 120 | // the key is stored as a node |
117 | else if((nodeMap & bitposition) != 0) { | 121 | else if ((nodeMap & bitposition) != 0) { |
118 | int keyIndex = content.length-1-index(nodeMap, bitposition); | 122 | int keyIndex = content.length - 1 - index(nodeMap, bitposition); |
119 | ImmutableNode<K,V> subNode = (ImmutableNode<K,V>) content[keyIndex]; | 123 | @SuppressWarnings("unchecked") |
120 | int newDepth = depth+1; | 124 | var subNode = (ImmutableNode<K, V>) content[keyIndex]; |
125 | int newDepth = depth + 1; | ||
121 | int newHash = newHash(hashProvider, key, hash, newDepth); | 126 | int newHash = newHash(hashProvider, key, hash, newDepth); |
122 | return subNode.getValue(key, hashProvider, defaultValue, newHash, newDepth); | 127 | return subNode.getValue(key, hashProvider, defaultValue, newHash, newDepth); |
123 | } | 128 | } |
@@ -126,21 +131,22 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
126 | return defaultValue; | 131 | return defaultValue; |
127 | } | 132 | } |
128 | } | 133 | } |
129 | 134 | ||
130 | @SuppressWarnings("unchecked") | ||
131 | @Override | 135 | @Override |
132 | public Node<K,V> putValue(K key, V value, OldValueBox<V> oldValue, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) { | 136 | public Node<K, V> putValue(K key, V value, OldValueBox<V> oldValue, ContinousHashProvider<? super K> hashProvider, |
133 | int selectedHashFragment = hashFragment(hash,shiftDepth(depth)); | 137 | V defaultValue, int hash, int depth) { |
138 | int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); | ||
134 | int bitposition = 1 << selectedHashFragment; | 139 | int bitposition = 1 << selectedHashFragment; |
135 | if((dataMap & bitposition) != 0) { | 140 | if ((dataMap & bitposition) != 0) { |
136 | int keyIndex = 2*index(dataMap, bitposition); | 141 | int keyIndex = 2 * index(dataMap, bitposition); |
142 | @SuppressWarnings("unchecked") | ||
137 | K keyCandidate = (K) content[keyIndex]; | 143 | K keyCandidate = (K) content[keyIndex]; |
138 | if(keyCandidate.equals(key)) { | 144 | if (keyCandidate.equals(key)) { |
139 | if(value == defaultValue) { | 145 | if (value == defaultValue) { |
140 | // delete | 146 | // delete |
141 | MutableNode<K, V> mutable = this.toMutable(); | 147 | MutableNode<K, V> mutable = this.toMutable(); |
142 | return mutable.removeEntry(selectedHashFragment,oldValue); | 148 | return mutable.removeEntry(selectedHashFragment, oldValue); |
143 | } else if(value == content[keyIndex+1]) { | 149 | } else if (value == content[keyIndex + 1]) { |
144 | // dont change | 150 | // dont change |
145 | oldValue.setOldValue(value); | 151 | oldValue.setOldValue(value); |
146 | return this; | 152 | return this; |
@@ -150,7 +156,7 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
150 | return mutable.updateValue(value, oldValue, selectedHashFragment); | 156 | return mutable.updateValue(value, oldValue, selectedHashFragment); |
151 | } | 157 | } |
152 | } else { | 158 | } else { |
153 | if(value == defaultValue) { | 159 | if (value == defaultValue) { |
154 | // dont change | 160 | // dont change |
155 | oldValue.setOldValue(defaultValue); | 161 | oldValue.setOldValue(defaultValue); |
156 | return this; | 162 | return this; |
@@ -160,19 +166,19 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
160 | return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); | 166 | return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); |
161 | } | 167 | } |
162 | } | 168 | } |
163 | } else if((nodeMap & bitposition)!=0) { | 169 | } else if ((nodeMap & bitposition) != 0) { |
164 | 170 | int keyIndex = content.length - 1 - index(nodeMap, bitposition); | |
165 | int keyIndex = content.length-1-index(nodeMap, bitposition); | 171 | @SuppressWarnings("unchecked") |
166 | ImmutableNode<K,V> subNode = (ImmutableNode<K,V>) content[keyIndex]; | 172 | var subNode = (ImmutableNode<K, V>) content[keyIndex]; |
167 | int newDepth = depth+1; | 173 | int newDepth = depth + 1; |
168 | int newHash = newHash(hashProvider, key, hash, newDepth); | 174 | int newHash = newHash(hashProvider, key, hash, newDepth); |
169 | Node<K,V> newsubNode = subNode.putValue(key, value, oldValue, hashProvider, defaultValue, newHash, newDepth); | 175 | var newsubNode = subNode.putValue(key, value, oldValue, hashProvider, defaultValue, newHash, newDepth); |
170 | 176 | ||
171 | if(subNode == newsubNode) { | 177 | if (subNode == newsubNode) { |
172 | // nothing changed | 178 | // nothing changed |
173 | return this; | 179 | return this; |
174 | } else { | 180 | } else { |
175 | MutableNode<K, V> mutable = toMutable(); | 181 | MutableNode<K, V> mutable = toMutable(); |
176 | return mutable.updateWithSubNode(selectedHashFragment, newsubNode, value.equals(defaultValue)); | 182 | return mutable.updateWithSubNode(selectedHashFragment, newsubNode, value.equals(defaultValue)); |
177 | } | 183 | } |
178 | } else { | 184 | } else { |
@@ -181,59 +187,56 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
181 | return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); | 187 | return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); |
182 | } | 188 | } |
183 | } | 189 | } |
184 | 190 | ||
185 | |||
186 | @SuppressWarnings("unchecked") | ||
187 | @Override | 191 | @Override |
188 | public long getSize() { | 192 | public long getSize() { |
189 | int result = Integer.bitCount(this.dataMap); | 193 | int result = Integer.bitCount(this.dataMap); |
190 | for(int subnodeIndex = 0; subnodeIndex < Integer.bitCount(this.nodeMap); subnodeIndex++) { | 194 | for (int subnodeIndex = 0; subnodeIndex < Integer.bitCount(this.nodeMap); subnodeIndex++) { |
191 | ImmutableNode<K,V> subnode = | 195 | @SuppressWarnings("unchecked") |
192 | (ImmutableNode<K, V>) this.content[this.content.length-1-subnodeIndex]; | 196 | var subnode = (ImmutableNode<K, V>) this.content[this.content.length - 1 - subnodeIndex]; |
193 | result += subnode.getSize(); | 197 | result += subnode.getSize(); |
194 | } | 198 | } |
195 | return result; | 199 | return result; |
196 | } | 200 | } |
197 | 201 | ||
198 | @Override | 202 | @Override |
199 | protected MutableNode<K,V> toMutable() { | 203 | protected MutableNode<K, V> toMutable() { |
200 | return new MutableNode<>(this); | 204 | return new MutableNode<>(this); |
201 | } | 205 | } |
202 | 206 | ||
203 | @Override | 207 | @Override |
204 | public ImmutableNode<K, V> toImmutable( | 208 | public ImmutableNode<K, V> toImmutable(Map<Node<K, V>, ImmutableNode<K, V>> cache) { |
205 | Map<Node<K, V>, ImmutableNode<K, V>> cache) { | ||
206 | return this; | 209 | return this; |
207 | } | 210 | } |
208 | 211 | ||
209 | @Override | 212 | @Override |
210 | protected MutableNode<K, V> isMutable() { | 213 | protected MutableNode<K, V> isMutable() { |
211 | return null; | 214 | return null; |
212 | } | 215 | } |
213 | 216 | ||
214 | @SuppressWarnings("unchecked") | 217 | @SuppressWarnings("unchecked") |
215 | @Override | 218 | @Override |
216 | boolean moveToNext(MapCursor<K, V> cursor) { | 219 | boolean moveToNext(MapCursor<K, V> cursor) { |
217 | // 1. try to move to data | 220 | // 1. try to move to data |
218 | int datas = Integer.bitCount(this.dataMap); | 221 | int datas = Integer.bitCount(this.dataMap); |
219 | if(cursor.dataIndex != MapCursor.INDEX_FINISH) { | 222 | if (cursor.dataIndex != MapCursor.INDEX_FINISH) { |
220 | int newDataIndex = cursor.dataIndex + 1; | 223 | int newDataIndex = cursor.dataIndex + 1; |
221 | if(newDataIndex < datas) { | 224 | if (newDataIndex < datas) { |
222 | cursor.dataIndex = newDataIndex; | 225 | cursor.dataIndex = newDataIndex; |
223 | cursor.key = (K) this.content[newDataIndex*2]; | 226 | cursor.key = (K) this.content[newDataIndex * 2]; |
224 | cursor.value = (V) this.content[newDataIndex*2+1]; | 227 | cursor.value = (V) this.content[newDataIndex * 2 + 1]; |
225 | return true; | 228 | return true; |
226 | } else { | 229 | } else { |
227 | cursor.dataIndex = MapCursor.INDEX_FINISH; | 230 | cursor.dataIndex = MapCursor.INDEX_FINISH; |
228 | } | 231 | } |
229 | } | 232 | } |
230 | 233 | ||
231 | // 2. look inside the subnodes | 234 | // 2. look inside the subnodes |
232 | int nodes = Integer.bitCount(this.nodeMap); | 235 | int nodes = Integer.bitCount(this.nodeMap); |
233 | int newNodeIndex = cursor.nodeIndexStack.peek() + 1; | 236 | int newNodeIndex = cursor.nodeIndexStack.peek() + 1; |
234 | if(newNodeIndex < nodes) { | 237 | if (newNodeIndex < nodes) { |
235 | // 2.1 found next subnode, move down to the subnode | 238 | // 2.1 found next subnode, move down to the subnode |
236 | Node<K, V> subnode = (Node<K, V>) this.content[this.content.length-1-newNodeIndex]; | 239 | Node<K, V> subnode = (Node<K, V>) this.content[this.content.length - 1 - newNodeIndex]; |
237 | cursor.dataIndex = MapCursor.INDEX_START; | 240 | cursor.dataIndex = MapCursor.INDEX_START; |
238 | cursor.nodeIndexStack.pop(); | 241 | cursor.nodeIndexStack.pop(); |
239 | cursor.nodeIndexStack.push(newNodeIndex); | 242 | cursor.nodeIndexStack.push(newNodeIndex); |
@@ -244,7 +247,7 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
244 | // 3. no subnode found, move up | 247 | // 3. no subnode found, move up |
245 | cursor.nodeStack.pop(); | 248 | cursor.nodeStack.pop(); |
246 | cursor.nodeIndexStack.pop(); | 249 | cursor.nodeIndexStack.pop(); |
247 | if(!cursor.nodeStack.isEmpty()) { | 250 | if (!cursor.nodeStack.isEmpty()) { |
248 | Node<K, V> supernode = cursor.nodeStack.peek(); | 251 | Node<K, V> supernode = cursor.nodeStack.peek(); |
249 | return supernode.moveToNext(cursor); | 252 | return supernode.moveToNext(cursor); |
250 | } else { | 253 | } else { |
@@ -254,69 +257,69 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
254 | } | 257 | } |
255 | } | 258 | } |
256 | } | 259 | } |
257 | 260 | ||
258 | @Override | 261 | @Override |
259 | public void prettyPrint(StringBuilder builder, int depth, int code) { | 262 | public void prettyPrint(StringBuilder builder, int depth, int code) { |
260 | for(int i = 0; i<depth; i++) { | 263 | for (int i = 0; i < depth; i++) { |
261 | builder.append("\t"); | 264 | builder.append("\t"); |
262 | } | 265 | } |
263 | if(code>=0) { | 266 | if (code >= 0) { |
264 | builder.append(code); | 267 | builder.append(code); |
265 | builder.append(":"); | 268 | builder.append(":"); |
266 | } | 269 | } |
267 | builder.append("Immutable("); | 270 | builder.append("Immutable("); |
268 | boolean hadContent = false; | 271 | boolean hadContent = false; |
269 | int dataMask = 1; | 272 | int dataMask = 1; |
270 | for(int i = 0; i<FACTOR; i++) { | 273 | for (int i = 0; i < FACTOR; i++) { |
271 | if((dataMask & dataMap) != 0) { | 274 | if ((dataMask & dataMap) != 0) { |
272 | if(hadContent) { | 275 | if (hadContent) { |
273 | builder.append(","); | 276 | builder.append(","); |
274 | } | 277 | } |
275 | builder.append(i); | 278 | builder.append(i); |
276 | builder.append(":["); | 279 | builder.append(":["); |
277 | builder.append(content[2*index(dataMap, dataMask)].toString()); | 280 | builder.append(content[2 * index(dataMap, dataMask)].toString()); |
278 | builder.append("]->["); | 281 | builder.append("]->["); |
279 | builder.append(content[2*index(dataMap, dataMask)+1].toString()); | 282 | builder.append(content[2 * index(dataMap, dataMask) + 1].toString()); |
280 | builder.append("]"); | 283 | builder.append("]"); |
281 | hadContent = true; | 284 | hadContent = true; |
282 | } | 285 | } |
283 | dataMask<<=1; | 286 | dataMask <<= 1; |
284 | } | 287 | } |
285 | builder.append(")"); | 288 | builder.append(")"); |
286 | int nodeMask = 1; | 289 | int nodeMask = 1; |
287 | for(int i = 0; i<FACTOR; i++) { | 290 | for (int i = 0; i < FACTOR; i++) { |
288 | if((nodeMask & nodeMap)!=0) { | 291 | if ((nodeMask & nodeMap) != 0) { |
289 | @SuppressWarnings("unchecked") | 292 | @SuppressWarnings("unchecked") |
290 | Node<K,V> subNode = (Node<K, V>) content[content.length-1-index(nodeMap, nodeMask)]; | 293 | Node<K, V> subNode = (Node<K, V>) content[content.length - 1 - index(nodeMap, nodeMask)]; |
291 | builder.append("\n"); | 294 | builder.append("\n"); |
292 | subNode.prettyPrint(builder, depth+1, i); | 295 | subNode.prettyPrint(builder, depth + 1, i); |
293 | } | 296 | } |
294 | nodeMask<<=1; | 297 | nodeMask <<= 1; |
295 | } | 298 | } |
296 | } | 299 | } |
297 | 300 | ||
298 | @SuppressWarnings("unchecked") | ||
299 | @Override | 301 | @Override |
300 | public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) { | 302 | public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) { |
301 | if(depth>0) { | 303 | if (depth > 0) { |
302 | boolean orphaned = Integer.bitCount(dataMap) == 1 && nodeMap == 0; | 304 | boolean orphaned = Integer.bitCount(dataMap) == 1 && nodeMap == 0; |
303 | if(orphaned) { | 305 | if (orphaned) { |
304 | throw new IllegalStateException("Orphaned node! " + dataMap + ": " + content[0]); | 306 | throw new IllegalStateException("Orphaned node! " + dataMap + ": " + content[0]); |
305 | } | 307 | } |
306 | } | 308 | } |
307 | // check the place of data | 309 | // check the place of data |
308 | 310 | ||
309 | // check subnodes | 311 | // check subnodes |
310 | for(int i = 0; i<Integer.bitCount(nodeMap); i++) { | 312 | for (int i = 0; i < Integer.bitCount(nodeMap); i++) { |
311 | Node<K,V> subnode = (Node<K, V>) this.content[this.content.length-1-i]; | 313 | @SuppressWarnings("unchecked") |
312 | if(! (subnode instanceof ImmutableNode<?,?>)) { | 314 | var subnode = (Node<K, V>) this.content[this.content.length - 1 - i]; |
315 | if (!(subnode instanceof ImmutableNode<?, ?>)) { | ||
313 | throw new IllegalStateException("Immutable node contains mutable subnodes!"); | 316 | throw new IllegalStateException("Immutable node contains mutable subnodes!"); |
314 | } else { | 317 | } else { |
315 | subnode.checkIntegrity(hashProvider, defaultValue, depth+1); | 318 | subnode.checkIntegrity(hashProvider, defaultValue, depth + 1); |
316 | } | 319 | } |
317 | } | 320 | } |
318 | } | 321 | } |
319 | 322 | ||
320 | @Override | 323 | @Override |
321 | public int hashCode() { | 324 | public int hashCode() { |
322 | return this.precalculatedHash; | 325 | return this.precalculatedHash; |
@@ -328,43 +331,39 @@ public class ImmutableNode<K, V> extends Node<K, V> { | |||
328 | return true; | 331 | return true; |
329 | if (obj == null) | 332 | if (obj == null) |
330 | return false; | 333 | return false; |
331 | if (obj instanceof ImmutableNode<?,?>) { | 334 | if (obj instanceof ImmutableNode<?, ?> other) { |
332 | ImmutableNode<?,?> other = (ImmutableNode<?,?>) obj; | 335 | return precalculatedHash == other.precalculatedHash && dataMap == other.dataMap && nodeMap == other.nodeMap |
333 | if (precalculatedHash != other.precalculatedHash || dataMap != other.dataMap || nodeMap != other.nodeMap || !Arrays.deepEquals(content, other.content)) | 336 | && Arrays.deepEquals(content, other.content); |
334 | return false; | 337 | } else if (obj instanceof MutableNode<?, ?> mutableObj) { |
335 | else return true; | 338 | return ImmutableNode.compareImmutableMutable(this, mutableObj); |
336 | } else if(obj instanceof MutableNode<?,?>) { | ||
337 | return ImmutableNode.compareImmutableMutable(this, (MutableNode<?, ?>) obj); | ||
338 | } else { | 339 | } else { |
339 | return false; | 340 | return false; |
340 | } | 341 | } |
341 | } | 342 | } |
342 | public static boolean compareImmutableMutable( | 343 | |
343 | ImmutableNode<?, ?> immutable, | 344 | public static boolean compareImmutableMutable(ImmutableNode<?, ?> immutable, MutableNode<?, ?> mutable) { |
344 | MutableNode<?, ?> mutable) | ||
345 | { | ||
346 | int datas = 0; | 345 | int datas = 0; |
347 | int nodes = 0; | 346 | int nodes = 0; |
348 | final int immutableLength = immutable.content.length; | 347 | final int immutableLength = immutable.content.length; |
349 | for(int i = 0; i<FACTOR; i++) { | 348 | for (int i = 0; i < FACTOR; i++) { |
350 | Object key = mutable.content[i*2]; | 349 | Object key = mutable.content[i * 2]; |
351 | // For each key candidate | 350 | // For each key candidate |
352 | if(key != null) { | 351 | if (key != null) { |
353 | // Check whether a new Key-Value pair can fit into the immutable container | 352 | // Check whether a new Key-Value pair can fit into the immutable container |
354 | if(datas*2+nodes+2 <= immutableLength) { | 353 | if (datas * 2 + nodes + 2 <= immutableLength) { |
355 | if( !immutable.content[datas*2].equals(key) || | 354 | if (!immutable.content[datas * 2].equals(key) |
356 | !immutable.content[datas*2+1].equals(mutable.content[i*2+1])) | 355 | || !immutable.content[datas * 2 + 1].equals(mutable.content[i * 2 + 1])) { |
357 | { | ||
358 | return false; | 356 | return false; |
359 | } | 357 | } |
360 | } else return false; | 358 | } else |
359 | return false; | ||
361 | datas++; | 360 | datas++; |
362 | } else { | 361 | } else { |
363 | Node<?,?> mutableSubnode = (Node<?, ?>) mutable.content[i*2+1]; | 362 | var mutableSubnode = (Node<?, ?>) mutable.content[i * 2 + 1]; |
364 | if(mutableSubnode != null) { | 363 | if (mutableSubnode != null) { |
365 | if(datas*2+nodes+1 <= immutableLength) { | 364 | if (datas * 2 + nodes + 1 <= immutableLength) { |
366 | Object immutableSubnode = immutable.content[immutableLength-1-nodes]; | 365 | Object immutableSubnode = immutable.content[immutableLength - 1 - nodes]; |
367 | if(!mutableSubnode.equals(immutableSubnode)) { | 366 | if (!mutableSubnode.equals(immutableSubnode)) { |
368 | return false; | 367 | return false; |
369 | } | 368 | } |
370 | nodes++; | 369 | nodes++; |