Skip to content

Commit c775bd5

Browse files
[MOD] XQuery: comparison of maps revised (simplified, faster)
1 parent 577e08e commit c775bd5

9 files changed

Lines changed: 39 additions & 129 deletions

File tree

basex-core/src/main/java/org/basex/query/util/DeepEqualOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public final class DeepEqualOptions extends Options {
4242
/** Option: items-equal. */
4343
public static final ValueOption ITEMS_EQUAL =
4444
new ValueOption("items-equal", SeqType.FUNCTION_O);
45+
/** Option: map-order. */
46+
public static final BooleanOption MAP_ORDER =
47+
new BooleanOption("map-order", false);
4548
/** Option: namespace-prefixes. */
4649
public static final BooleanOption NAMESPACE_PREFIXES =
4750
new BooleanOption("namespace-prefixes", false);

basex-core/src/main/java/org/basex/query/value/map/TrieBranch.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.basex.query.value.map;
22

33
import org.basex.query.*;
4-
import org.basex.query.util.*;
54
import org.basex.query.value.*;
65
import org.basex.query.value.item.*;
76
import org.basex.util.*;
@@ -83,22 +82,6 @@ Value get(final int hs, final Item ky, final int lv) throws QueryException {
8382
return sub == null ? null : sub.get(hs, ky, lv + 1);
8483
}
8584

86-
@Override
87-
boolean equal(final TrieNode node, final DeepEqual deep) throws QueryException {
88-
if(!(node instanceof TrieBranch)) return false;
89-
90-
// check bit usage first
91-
final TrieBranch ob = (TrieBranch) node;
92-
if(used != ob.used) return false;
93-
// recursively compare children
94-
if(deep != null && deep.qc != null) deep.qc.checkStop();
95-
for(int k = 0; k < KIDS; k++) {
96-
if(kids[k] != null && !kids[k].equal(ob.kids[k], deep)) return false;
97-
}
98-
// everything OK
99-
return true;
100-
}
101-
10285
/** End strings. */
10386
private static final String[] ENDS = { "|-- ", "| ", "`-- ", " " };
10487

basex-core/src/main/java/org/basex/query/value/map/TrieEmpty.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.basex.query.value.map;
22

3-
import org.basex.query.util.*;
43
import org.basex.query.value.*;
54
import org.basex.query.value.item.*;
65
import org.basex.util.*;
@@ -37,11 +36,6 @@ Value get(final int hs, final Item ky, final int lv) {
3736
return null;
3837
}
3938

40-
@Override
41-
boolean equal(final TrieNode node, final DeepEqual deep) {
42-
return this == node;
43-
}
44-
4539
@Override
4640
void add(final TokenBuilder tb, final String indent) {
4741
tb.add("{}");

basex-core/src/main/java/org/basex/query/value/map/TrieLeaf.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.basex.query.value.map;
22

33
import org.basex.query.*;
4-
import org.basex.query.util.*;
54
import org.basex.query.value.*;
65
import org.basex.query.value.item.*;
76
import org.basex.util.*;
@@ -66,16 +65,6 @@ Value get(final int hs, final Item ky, final int lv) throws QueryException {
6665
return hs == hash && key.atomicEqual(ky) ? value : null;
6766
}
6867

69-
@Override
70-
boolean equal(final TrieNode node, final DeepEqual deep) throws QueryException {
71-
if(node instanceof TrieLeaf) {
72-
final TrieLeaf leaf = (TrieLeaf) node;
73-
return deep != null ? key.atomicEqual(leaf.key) && deep.equal(value, leaf.value) :
74-
key.equals(leaf.key) && value.equals(leaf.value);
75-
}
76-
return false;
77-
}
78-
7968
@Override
8069
void add(final TokenBuilder tb, final String indent) {
8170
tb.add(indent).add("`-- ").add(key).add(" => ").add(value).add('\n');

basex-core/src/main/java/org/basex/query/value/map/TrieList.java

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.basex.query.value.map;
22

33
import org.basex.query.*;
4-
import org.basex.query.util.*;
54
import org.basex.query.value.*;
65
import org.basex.query.value.item.*;
76
import org.basex.util.*;
@@ -15,7 +14,6 @@
1514
final class TrieList extends TrieNode {
1615
/** Common hash value of all contained values. */
1716
final int hash;
18-
1917
/** List of keys of this collision list. */
2018
final Item[] keys;
2119
/** List of values of this collision list. */
@@ -109,34 +107,6 @@ Value get(final int hs, final Item ky, final int lv) throws QueryException {
109107
return null;
110108
}
111109

112-
@Override
113-
boolean equal(final TrieNode node, final DeepEqual deep) throws QueryException {
114-
if(!(node instanceof TrieList) || size != node.size) return false;
115-
116-
// do the evil nested-loop thing
117-
final TrieList ol = (TrieList) node;
118-
OUTER:
119-
for(int i = 0; i < size; i++) {
120-
if(deep != null && deep.qc != null) deep.qc.checkStop();
121-
final Item key = keys[i];
122-
final Value value = values[i];
123-
for(int j = 0; j < size; j++) {
124-
if(deep != null) {
125-
if(!key.atomicEqual(ol.keys[j])) continue;
126-
if(!deep.equal(value, ol.values[j])) return false;
127-
} else {
128-
if(!key.equals(ol.keys[j])) continue;
129-
if(!value.equals(ol.values[j])) return false;
130-
}
131-
continue OUTER;
132-
}
133-
// all keys of the other list were checked, none matched
134-
return false;
135-
}
136-
// all entries were found
137-
return true;
138-
}
139-
140110
@Override
141111
void add(final TokenBuilder tb, final String indent) {
142112
tb.add(indent).add("`-- Collision (").add(Integer.toHexString(hash)).add("):\n");

basex-core/src/main/java/org/basex/query/value/map/TrieNode.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.basex.query.value.map;
22

33
import org.basex.query.*;
4-
import org.basex.query.util.*;
54
import org.basex.query.value.*;
65
import org.basex.query.value.item.*;
76
import org.basex.util.*;
@@ -98,15 +97,6 @@ static int hashKey(final int hash, final int level) {
9897
return hash >>> level * BITS & MASK;
9998
}
10099

101-
/**
102-
* Checks if this node is indistinguishable from the given node.
103-
* @param node other node
104-
* @param deep comparator
105-
* @return result of check
106-
* @throws QueryException query exception
107-
*/
108-
abstract boolean equal(TrieNode node, DeepEqual deep) throws QueryException;
109-
110100
/**
111101
* Recursive {@link #toString()} helper.
112102
* @param tb token builder

basex-core/src/main/java/org/basex/query/value/map/XQHashMap.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import org.basex.query.*;
44
import org.basex.query.expr.*;
55
import org.basex.query.iter.*;
6-
import org.basex.query.util.*;
76
import org.basex.query.util.hash.*;
87
import org.basex.query.value.*;
98
import org.basex.query.value.item.*;
@@ -79,23 +78,6 @@ public Value value(final QueryContext qc, final Expr expr) {
7978
};
8079
}
8180

82-
@Override
83-
public boolean deepEqual(final XQMap compare, final DeepEqual deep) throws QueryException {
84-
final ItemObjectMap<Value> map2 = ((XQHashMap) compare).map;
85-
final int is = map.size();
86-
for(int i = 1; i <= is; i++) {
87-
final Item key = map.key(i), key2 = map2.key(i);
88-
final Value value = map.value(i), value2 = map2.value(i);
89-
if(deep != null) {
90-
// different order: call fallback
91-
if(!(key.atomicEqual(key2) && deep.equal(value, value2))) return deepEq(compare, deep);
92-
} else {
93-
if(!(key.equals(key2) && value.equals(value2))) return false;
94-
}
95-
}
96-
return true;
97-
}
98-
9981
/**
10082
* Transforms the map to an immutable representation.
10183
* @return map

basex-core/src/main/java/org/basex/query/value/map/XQMap.java

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -368,53 +368,58 @@ public final boolean deepEqual(final Item item, final DeepEqual deep) throws Que
368368
if(!(item instanceof XQMap)) return false;
369369
final XQMap map = (XQMap) item;
370370
if(structSize() != map.structSize()) return false;
371-
// compare identical map representations first (faster)
372-
return getClass() == map.getClass() ? deepEqual(map, deep) : deepEq(map, deep);
371+
// compilation: ordered comparison
372+
if(deep == null) return deepEqualOrdered(map);
373+
// deep equality: always compare ordered first (faster)
374+
return deepEqualOrdered(map, deep) ||
375+
!deep.options.get(DeepEqualOptions.MAP_ORDER) && deepEqualUnordered(map, deep);
373376
}
374377

375378
/**
376-
* Compares two maps for equality (fallback).
379+
* Compares two maps for equality, considering order, at compile time.
377380
* @param map map to be compared
378-
* @param deep comparator (can be {@code null})
379381
* @return result of check
380382
* @throws QueryException query exception
381383
*/
382-
final boolean deepEq(final XQMap map, final DeepEqual deep) throws QueryException {
383-
// compare sorted entries
384-
final BasicIter<Item> keys = keys(), keys2 = map.keys();
385-
while(true) {
386-
final Item key = keys.next(), key2 = keys2.next();
387-
if(key == null) return true;
388-
final Value value = getInternal(key), value2 = map.getInternal(key2);
389-
if(deep != null) {
390-
if(!(key.atomicEqual(key2) && deep.equal(value, value2))) break;
391-
} else {
392-
if(!(key.equals(key2) && value.equals(value2))) break;
393-
}
384+
private boolean deepEqualOrdered(final XQMap map) throws QueryException {
385+
final BasicIter<Item> keys2 = map.keys();
386+
for(final Item key : keys()) {
387+
final Item k = keys2.next();
388+
if(!(key.equals(k) && getInternal(key).equals(map.getInternal(k)))) return false;
394389
}
395-
// compare unsorted entries (worst case)
396-
return test((key, value) -> {
397-
if(deep != null && deep.qc != null) deep.qc.checkStop();
398-
for(final Item key2 : map.keys()) {
399-
final Value value2 = map.getInternal(key2);
400-
if(deep != null) {
401-
if(key.atomicEqual(key2)) return deep.equal(value, value2);
402-
} else {
403-
if(key.equals(key2)) return value.equals(value2);
404-
}
405-
}
406-
return false;
407-
});
390+
return true;
391+
}
392+
393+
/**
394+
* Compares two maps for equality, considering order.
395+
* @param map map to be compared
396+
* @param deep comparator
397+
* @return result of check
398+
* @throws QueryException query exception
399+
*/
400+
private boolean deepEqualOrdered(final XQMap map, final DeepEqual deep)throws QueryException {
401+
final BasicIter<Item> keys2 = map.keys();
402+
for(final Item key : keys()) {
403+
final Item k = keys2.next();
404+
if(!(key.atomicEqual(k) && deep.equal(getInternal(key), map.getInternal(k)))) return false;
405+
}
406+
return true;
408407
}
409408

410409
/**
411-
* Compares two maps for equality.
410+
* Compares two maps for equality, ignoring order.
412411
* @param map map to be compared
413412
* @param deep comparator
414413
* @return result of check
415414
* @throws QueryException query exception
416415
*/
417-
public abstract boolean deepEqual(XQMap map, DeepEqual deep) throws QueryException;
416+
private boolean deepEqualUnordered(final XQMap map, final DeepEqual deep) throws QueryException {
417+
for(final Item key : keys()) {
418+
final Value v = map.getInternal(key);
419+
if(v == null || !deep.equal(getInternal(key), v)) return false;
420+
}
421+
return true;
422+
}
418423

419424
@Override
420425
public final void string(final boolean indent, final TokenBuilder tb, final int level,

basex-core/src/main/java/org/basex/query/value/map/XQTrieMap.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import org.basex.query.*;
44
import org.basex.query.iter.*;
5-
import org.basex.query.util.*;
65
import org.basex.query.value.*;
76
import org.basex.query.value.item.*;
87
import org.basex.query.value.seq.*;
@@ -79,9 +78,4 @@ public BasicIter<Item> keys() {
7978
final long size = structSize();
8079
return size == 0 ? Empty.ITER : size == 1 ? ((TrieLeaf) root).key.iter() : order.keys();
8180
}
82-
83-
@Override
84-
public boolean deepEqual(final XQMap map, final DeepEqual deep) throws QueryException {
85-
return root.equal(((XQTrieMap) map).root, deep);
86-
}
8781
}

0 commit comments

Comments
 (0)