diff options
author | Kristóf Marussy <marussy@mit.bme.hu> | 2021-09-29 02:45:57 +0200 |
---|---|---|
committer | Kristóf Marussy <marussy@mit.bme.hu> | 2021-09-29 03:16:01 +0200 |
commit | a155f6ba02e08a75ce6e474a86900b8363f506e8 (patch) | |
tree | b78804c1c0f0968a9625f0656e08f5dadc16924c /store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java | |
parent | Simplify branding (diff) | |
download | refinery-a155f6ba02e08a75ce6e474a86900b8363f506e8.tar.gz refinery-a155f6ba02e08a75ce6e474a86900b8363f506e8.tar.zst refinery-a155f6ba02e08a75ce6e474a86900b8363f506e8.zip |
build: migration to Gradle 7
Diffstat (limited to 'store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java')
-rw-r--r-- | store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java new file mode 100644 index 00000000..9e63b941 --- /dev/null +++ b/store/src/main/java/org/eclipse/viatra/solver/data/map/internal/MutableNode.java | |||
@@ -0,0 +1,449 @@ | |||
1 | package org.eclipse.viatra.solver.data.map.internal; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.Map; | ||
5 | |||
6 | import org.eclipse.viatra.solver.data.map.ContinousHashProvider; | ||
7 | |||
8 | public class MutableNode<K, V> extends Node<K, V> { | ||
9 | int cachedHash; | ||
10 | protected Object[] content; | ||
11 | |||
12 | protected MutableNode() { | ||
13 | this.content = new Object[2 * FACTOR]; | ||
14 | updateHash(); | ||
15 | } | ||
16 | |||
17 | public static <K, V> MutableNode<K, V> initialize(K key, V value, ContinousHashProvider<? super K> hashProvider, | ||
18 | V defaultValue) { | ||
19 | if (value == defaultValue) { | ||
20 | return null; | ||
21 | } else { | ||
22 | int hash = hashProvider.getHash(key, 0); | ||
23 | int fragment = hashFragment(hash, 0); | ||
24 | MutableNode<K, V> res = new MutableNode<>(); | ||
25 | res.content[2 * fragment] = key; | ||
26 | res.content[2 * fragment + 1] = value; | ||
27 | res.updateHash(); | ||
28 | return res; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode} | ||
34 | * | ||
35 | * @param node | ||
36 | */ | ||
37 | protected MutableNode(ImmutableNode<K, V> node) { | ||
38 | this.content = new Object[2 * FACTOR]; | ||
39 | int dataUsed = 0; | ||
40 | int nodeUsed = 0; | ||
41 | for (int i = 0; i < FACTOR; i++) { | ||
42 | int bitposition = 1 << i; | ||
43 | if ((node.dataMap & bitposition) != 0) { | ||
44 | content[2 * i] = node.content[dataUsed * 2]; | ||
45 | content[2 * i + 1] = node.content[dataUsed * 2 + 1]; | ||
46 | dataUsed++; | ||
47 | } else if ((node.nodeMap & bitposition) != 0) { | ||
48 | content[2 * i + 1] = node.content[node.content.length - 1 - nodeUsed]; | ||
49 | nodeUsed++; | ||
50 | } | ||
51 | } | ||
52 | this.cachedHash = node.hashCode(); | ||
53 | } | ||
54 | |||
55 | @SuppressWarnings("unchecked") | ||
56 | @Override | ||
57 | public V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth) { | ||
58 | int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); | ||
59 | K keyCandidate = (K) this.content[2 * selectedHashFragment]; | ||
60 | if (keyCandidate != null) { | ||
61 | if (keyCandidate.equals(key)) { | ||
62 | return (V) this.content[2 * selectedHashFragment + 1]; | ||
63 | } else { | ||
64 | return defaultValue; | ||
65 | } | ||
66 | } else { | ||
67 | Node<K, V> nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1]; | ||
68 | if (nodeCandidate != null) { | ||
69 | int newDepth = depth + 1; | ||
70 | int newHash = newHash(hashProvider, key, hash, newDepth); | ||
71 | return nodeCandidate.getValue(key, hashProvider, defaultValue, newHash, newDepth); | ||
72 | } else { | ||
73 | return defaultValue; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | @SuppressWarnings("unchecked") | ||
79 | @Override | ||
80 | public Node<K, V> putValue(K key, V value, OldValueBox<V> oldValue, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, | ||
81 | int depth) { | ||
82 | int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); | ||
83 | K keyCandidate = (K) content[2 * selectedHashFragment]; | ||
84 | if (keyCandidate != null) { | ||
85 | // If has key | ||
86 | if (keyCandidate.equals(key)) { | ||
87 | // The key is equals to an existing key -> update entry | ||
88 | if (value == defaultValue) { | ||
89 | return removeEntry(selectedHashFragment, oldValue); | ||
90 | } else { | ||
91 | return updateValue(value, oldValue, selectedHashFragment); | ||
92 | } | ||
93 | } else { | ||
94 | // The key is not equivalent to an existing key on the same hash bin | ||
95 | // -> split entry if it is necessary | ||
96 | if (value == defaultValue) { | ||
97 | // Value is default -> do not need to add new node | ||
98 | oldValue.setOldValue(defaultValue); | ||
99 | return this; | ||
100 | } else { | ||
101 | // Value is not default -> Split entry data to a new node | ||
102 | oldValue.setOldValue(defaultValue); | ||
103 | return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment); | ||
104 | } | ||
105 | } | ||
106 | } else { | ||
107 | // If it does not have key, check for value | ||
108 | Node<K, V> nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1]; | ||
109 | if (nodeCandidate != null) { | ||
110 | // If it has value, it is a subnode -> upate that | ||
111 | Node<K, V> newNode = nodeCandidate.putValue(key, value, oldValue, hashProvider, defaultValue, | ||
112 | newHash(hashProvider, key, hash, depth + 1), depth + 1); | ||
113 | return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue)); | ||
114 | } else { | ||
115 | // If it does not have value, put it in the empty place | ||
116 | if (value == defaultValue) { | ||
117 | // dont need to add new key-value pair | ||
118 | oldValue.setOldValue(defaultValue); | ||
119 | return this; | ||
120 | } else { | ||
121 | return addEntry(key, value, oldValue, selectedHashFragment); | ||
122 | } | ||
123 | |||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | @SuppressWarnings("unchecked") | ||
129 | private Node<K, V> addEntry(K key, V value, OldValueBox<V> oldValue, int selectedHashFragment) { | ||
130 | content[2 * selectedHashFragment] = key; | ||
131 | oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); | ||
132 | content[2 * selectedHashFragment + 1] = value; | ||
133 | updateHash(); | ||
134 | return this; | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * Updates an entry in a selected hash-fragment to a non-default value. | ||
139 | * | ||
140 | * @param value | ||
141 | * @param selectedHashFragment | ||
142 | * @return | ||
143 | */ | ||
144 | @SuppressWarnings("unchecked") | ||
145 | Node<K, V> updateValue(V value, OldValueBox<V> oldValue, int selectedHashFragment) { | ||
146 | oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); | ||
147 | content[2 * selectedHashFragment + 1] = value; | ||
148 | updateHash(); | ||
149 | return this; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * | ||
154 | * @param selectedHashFragment | ||
155 | * @param newNode | ||
156 | * @return | ||
157 | */ | ||
158 | Node<K, V> updateWithSubNode(int selectedHashFragment, Node<K, V> newNode, boolean deletionHappened) { | ||
159 | if (deletionHappened) { | ||
160 | if (newNode == null) { | ||
161 | // Check whether this node become empty | ||
162 | content[2 * selectedHashFragment + 1] = null; // i.e. the new node | ||
163 | if (hasContent()) { | ||
164 | updateHash(); | ||
165 | return this; | ||
166 | } else { | ||
167 | return null; | ||
168 | } | ||
169 | } else { | ||
170 | // check whether newNode is orphan | ||
171 | MutableNode<K, V> immutableNewNode = newNode.isMutable(); | ||
172 | if (immutableNewNode != null) { | ||
173 | int orphaned = immutableNewNode.isOrphaned(); | ||
174 | if (orphaned >= 0) { | ||
175 | // orphan subnode data is replaced with data | ||
176 | content[2 * selectedHashFragment] = immutableNewNode.content[orphaned * 2]; | ||
177 | content[2 * selectedHashFragment + 1] = immutableNewNode.content[orphaned * 2 + 1]; | ||
178 | updateHash(); | ||
179 | return this; | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | // normal behaviour | ||
185 | content[2 * selectedHashFragment + 1] = newNode; | ||
186 | updateHash(); | ||
187 | return this; | ||
188 | |||
189 | } | ||
190 | |||
191 | private boolean hasContent() { | ||
192 | for (Object element : this.content) { | ||
193 | if (element != null) | ||
194 | return true; | ||
195 | } | ||
196 | return false; | ||
197 | } | ||
198 | |||
199 | @Override | ||
200 | protected MutableNode<K, V> isMutable() { | ||
201 | return this; | ||
202 | } | ||
203 | |||
204 | protected int isOrphaned() { | ||
205 | int dataFound = -2; | ||
206 | for (int i = 0; i < FACTOR; i++) { | ||
207 | if (content[i * 2] != null) { | ||
208 | if (dataFound >= 0) { | ||
209 | return -1; | ||
210 | } else { | ||
211 | dataFound = i; | ||
212 | } | ||
213 | } else if (content[i * 2 + 1] != null) { | ||
214 | return -3; | ||
215 | } | ||
216 | } | ||
217 | return dataFound; | ||
218 | } | ||
219 | |||
220 | @SuppressWarnings("unchecked") | ||
221 | private Node<K, V> moveDownAndSplit(ContinousHashProvider<? super K> hashProvider, K newKey, V newValue, | ||
222 | K previousKey, int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth) { | ||
223 | V previousValue = (V) content[2 * selectedHashFragmentOfCurrentDepth + 1]; | ||
224 | |||
225 | MutableNode<K, V> newSubNode = newNodeWithTwoEntries(hashProvider, previousKey, previousValue, | ||
226 | hashProvider.getHash(previousKey, hashDepth(depth)), newKey, newValue, hashOfNewKey, depth + 1); | ||
227 | |||
228 | content[2 * selectedHashFragmentOfCurrentDepth] = null; | ||
229 | content[2 * selectedHashFragmentOfCurrentDepth + 1] = newSubNode; | ||
230 | updateHash(); | ||
231 | return this; | ||
232 | } | ||
233 | |||
234 | private MutableNode<K, V> newNodeWithTwoEntries(ContinousHashProvider<? super K> hashProvider, K key1, V value1, | ||
235 | int oldHash1, K key2, V value2, int oldHash2, int newdepth) { | ||
236 | int newHash1 = newHash(hashProvider, key1, oldHash1, newdepth); | ||
237 | int newHash2 = newHash(hashProvider, key2, oldHash2, newdepth); | ||
238 | int newFragment1 = hashFragment(newHash1, shiftDepth(newdepth)); | ||
239 | int newFragment2 = hashFragment(newHash2, shiftDepth(newdepth)); | ||
240 | |||
241 | MutableNode<K, V> subNode = new MutableNode<>(); | ||
242 | if (newFragment1 != newFragment2) { | ||
243 | subNode.content[newFragment1 * 2] = key1; | ||
244 | subNode.content[newFragment1 * 2 + 1] = value1; | ||
245 | |||
246 | subNode.content[newFragment2 * 2] = key2; | ||
247 | subNode.content[newFragment2 * 2 + 1] = value2; | ||
248 | } else { | ||
249 | MutableNode<K, V> subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2, | ||
250 | newHash2, newdepth + 1); | ||
251 | subNode.content[newFragment1 * 2 + 1] = subSubNode; | ||
252 | } | ||
253 | subNode.updateHash(); | ||
254 | return subNode; | ||
255 | } | ||
256 | |||
257 | @SuppressWarnings("unchecked") | ||
258 | Node<K, V> removeEntry(int selectedHashFragment, OldValueBox<V> oldValue) { | ||
259 | content[2 * selectedHashFragment] = null; | ||
260 | oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); | ||
261 | content[2 * selectedHashFragment + 1] = null; | ||
262 | if (hasContent()) { | ||
263 | updateHash(); | ||
264 | return this; | ||
265 | } else { | ||
266 | return null; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | @SuppressWarnings("unchecked") | ||
271 | @Override | ||
272 | public long getSize() { | ||
273 | int size = 0; | ||
274 | for (int i = 0; i < FACTOR; i++) { | ||
275 | if (content[i * 2] != null) { | ||
276 | size++; | ||
277 | } else { | ||
278 | Node<K, V> nodeCandidate = (Node<K, V>) content[i * 2 + 1]; | ||
279 | if (nodeCandidate != null) { | ||
280 | size += nodeCandidate.getSize(); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | return size; | ||
285 | } | ||
286 | |||
287 | @Override | ||
288 | protected MutableNode<K, V> toMutable() { | ||
289 | return this; | ||
290 | } | ||
291 | |||
292 | @Override | ||
293 | public ImmutableNode<K, V> toImmutable(Map<Node<K, V>, ImmutableNode<K, V>> cache) { | ||
294 | return ImmutableNode.constructImmutable(this, cache); | ||
295 | } | ||
296 | |||
297 | @SuppressWarnings("unchecked") | ||
298 | @Override | ||
299 | boolean moveToNext(MapCursor<K, V> cursor) { | ||
300 | // 1. try to move to data | ||
301 | if (cursor.dataIndex != MapCursor.INDEX_FINISH) { | ||
302 | for (int index = cursor.dataIndex + 1; index < FACTOR; index++) { | ||
303 | if (this.content[index * 2] != null) { | ||
304 | // 1.1 found next data | ||
305 | cursor.dataIndex = index; | ||
306 | cursor.key = (K) this.content[index * 2]; | ||
307 | cursor.value = (V) this.content[index * 2 + 1]; | ||
308 | return true; | ||
309 | } | ||
310 | } | ||
311 | cursor.dataIndex = MapCursor.INDEX_FINISH; | ||
312 | } | ||
313 | |||
314 | // 2. look inside the subnodes | ||
315 | for (int index = cursor.nodeIndexStack.peek() + 1; index < FACTOR; index++) { | ||
316 | if (this.content[index * 2] == null && this.content[index * 2 + 1] != null) { | ||
317 | // 2.1 found next subnode, move down to the subnode | ||
318 | Node<K, V> subnode = (Node<K, V>) this.content[index * 2 + 1]; | ||
319 | |||
320 | cursor.dataIndex = MapCursor.INDEX_START; | ||
321 | cursor.nodeIndexStack.pop(); | ||
322 | cursor.nodeIndexStack.push(index); | ||
323 | cursor.nodeIndexStack.push(MapCursor.INDEX_START); | ||
324 | cursor.nodeStack.push(subnode); | ||
325 | |||
326 | return subnode.moveToNext(cursor); | ||
327 | } | ||
328 | } | ||
329 | // 3. no subnode found, move up | ||
330 | cursor.nodeStack.pop(); | ||
331 | cursor.nodeIndexStack.pop(); | ||
332 | if (!cursor.nodeStack.isEmpty()) { | ||
333 | Node<K, V> supernode = cursor.nodeStack.peek(); | ||
334 | return supernode.moveToNext(cursor); | ||
335 | } else { | ||
336 | cursor.key = null; | ||
337 | cursor.value = null; | ||
338 | return false; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | @Override | ||
343 | public void prettyPrint(StringBuilder builder, int depth, int code) { | ||
344 | for (int i = 0; i < depth; i++) { | ||
345 | builder.append("\t"); | ||
346 | } | ||
347 | if (code >= 0) { | ||
348 | builder.append(code); | ||
349 | builder.append(":"); | ||
350 | } | ||
351 | builder.append("Mutable("); | ||
352 | // print content | ||
353 | boolean hadContent = false; | ||
354 | for (int i = 0; i < FACTOR; i++) { | ||
355 | if (content[2 * i] != null) { | ||
356 | if (hadContent) { | ||
357 | builder.append(","); | ||
358 | } | ||
359 | builder.append(i); | ||
360 | builder.append(":["); | ||
361 | builder.append(content[2 * i].toString()); | ||
362 | builder.append("]->["); | ||
363 | builder.append(content[2 * i + 1].toString()); | ||
364 | builder.append("]"); | ||
365 | hadContent = true; | ||
366 | } | ||
367 | } | ||
368 | builder.append(")"); | ||
369 | // print subnodes | ||
370 | for (int i = 0; i < FACTOR; i++) { | ||
371 | if (content[2 * i] == null && content[2 * i + 1] != null) { | ||
372 | @SuppressWarnings("unchecked") | ||
373 | Node<K, V> subNode = (Node<K, V>) content[2 * i + 1]; | ||
374 | builder.append("\n"); | ||
375 | subNode.prettyPrint(builder, depth + 1, i); | ||
376 | } | ||
377 | } | ||
378 | } | ||
379 | |||
380 | @SuppressWarnings({ "unchecked" }) | ||
381 | @Override | ||
382 | public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) { | ||
383 | // check for orphan nodes | ||
384 | if (depth > 0) { | ||
385 | int orphaned = isOrphaned(); | ||
386 | if (orphaned >= 0) { | ||
387 | throw new IllegalStateException("Orphaned node! " + orphaned + ": " + content[2 * orphaned]); | ||
388 | } | ||
389 | } | ||
390 | // check the place of data | ||
391 | for (int i = 0; i < FACTOR; i++) { | ||
392 | if (this.content[2 * i] != null) { | ||
393 | K key = (K) this.content[2 * i]; | ||
394 | V value = (V) this.content[2 * i + 1]; | ||
395 | |||
396 | if (value == defaultValue) { | ||
397 | throw new IllegalStateException("Node contains default value!"); | ||
398 | } | ||
399 | int hashCode = hashProvider.getHash(key, hashDepth(depth)); | ||
400 | int shiftDepth = shiftDepth(depth); | ||
401 | int selectedHashFragment = hashFragment(hashCode, shiftDepth); | ||
402 | if (i != selectedHashFragment) { | ||
403 | throw new IllegalStateException("Key " + key + " with hash code " + hashCode | ||
404 | + " is in bad place! Fragment=" + selectedHashFragment + ", Place=" + i); | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | // check subnodes | ||
409 | for (int i = 0; i < FACTOR; i++) { | ||
410 | if (this.content[2 * i + 1] != null && this.content[2 * i] == null) { | ||
411 | Node<K, V> subNode = (Node<K, V>) this.content[2 * i + 1]; | ||
412 | subNode.checkIntegrity(hashProvider, defaultValue, depth + 1); | ||
413 | } | ||
414 | } | ||
415 | // check the hash | ||
416 | int oldHash = this.cachedHash; | ||
417 | updateHash(); | ||
418 | int newHash = this.cachedHash; | ||
419 | if (oldHash != newHash) { | ||
420 | throw new IllegalStateException("Hash code was not up to date! (old=" + oldHash + ",new=" + newHash + ")"); | ||
421 | } | ||
422 | } | ||
423 | |||
424 | protected void updateHash() { | ||
425 | this.cachedHash = Arrays.hashCode(content); | ||
426 | } | ||
427 | |||
428 | @Override | ||
429 | public int hashCode() { | ||
430 | return this.cachedHash; | ||
431 | } | ||
432 | |||
433 | @Override | ||
434 | public boolean equals(Object obj) { | ||
435 | if (this == obj) | ||
436 | return true; | ||
437 | if (obj == null) | ||
438 | return false; | ||
439 | if (obj instanceof MutableNode<?, ?>) { | ||
440 | MutableNode<?, ?> other = (MutableNode<?, ?>) obj; | ||
441 | return Arrays.deepEquals(this.content, other.content); | ||
442 | } else if (obj instanceof ImmutableNode<?, ?>) { | ||
443 | ImmutableNode<?, ?> other = (ImmutableNode<?, ?>) obj; | ||
444 | return ImmutableNode.compareImmutableMutable(other, this); | ||
445 | } else { | ||
446 | return false; | ||
447 | } | ||
448 | } | ||
449 | } | ||