diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java index 2b512ad5e..224a51f52 100755 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfigSet.java @@ -30,6 +30,7 @@ package org.antlr.v4.runtime.atn; +import org.antlr.v4.runtime.misc.AbstractEqualityComparator; import org.antlr.v4.runtime.misc.Array2DHashSet; import org.antlr.v4.runtime.misc.DoubleKeyMap; @@ -239,7 +240,14 @@ public class ATNConfigSet implements Set { */ public static class ConfigHashSet extends Array2DHashSet { public ConfigHashSet() { - super(16,2); + super(ConfigEqualityComparator.INSTANCE,16,2); + } + } + + public static final class ConfigEqualityComparator extends AbstractEqualityComparator { + public static final ConfigEqualityComparator INSTANCE = new ConfigEqualityComparator(); + + private ConfigEqualityComparator() { } @Override @@ -273,7 +281,7 @@ public class ATNConfigSet implements Set { /** All configs but hashed by (s, i, _, pi) not incl context. Wiped out * when we go readonly as this set becomes a DFA state. */ - public ConfigHashSet configLookup; + public Array2DHashSet configLookup; /** Track the elements as they are added to the set; supports get(i) */ public final ArrayList configs = new ArrayList(7); diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/OrderedATNConfigSet.java b/runtime/Java/src/org/antlr/v4/runtime/atn/OrderedATNConfigSet.java index e94fe5323..7d2e9ad6a 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/OrderedATNConfigSet.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/OrderedATNConfigSet.java @@ -30,6 +30,9 @@ package org.antlr.v4.runtime.atn; +import org.antlr.v4.runtime.misc.Array2DHashSet; +import org.antlr.v4.runtime.misc.ObjectEqualityComparator; + /** * * @author Sam Harwell @@ -40,18 +43,9 @@ public class OrderedATNConfigSet extends ATNConfigSet { this.configLookup = new LexerConfigHashSet(); } - protected static class LexerConfigHashSet extends ConfigHashSet { - - @Override - public int hashCode(ATNConfig o) { - return o.hashCode(); + public static class LexerConfigHashSet extends Array2DHashSet { + public LexerConfigHashSet() { + super(ObjectEqualityComparator.INSTANCE, 16, 2); } - - @Override - public boolean equals(ATNConfig a, ATNConfig b) { - return a.equals(b); - } - } - } diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java index 3d25894da..97db0c5ae 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionMode.java @@ -1,5 +1,6 @@ package org.antlr.v4.runtime.atn; +import org.antlr.v4.runtime.misc.AbstractEqualityComparator; import org.antlr.v4.runtime.misc.FlexibleHashMap; import org.antlr.v4.runtime.misc.NotNull; @@ -34,6 +35,17 @@ public enum PredictionMode { /** A Map that uses just the state and the stack context as the key. */ static class AltAndContextMap extends FlexibleHashMap { + public AltAndContextMap() { + super(AltAndContextConfigEqualityComparator.INSTANCE); + } + } + + private static final class AltAndContextConfigEqualityComparator extends AbstractEqualityComparator { + public static final AltAndContextConfigEqualityComparator INSTANCE = new AltAndContextConfigEqualityComparator(); + + private AltAndContextConfigEqualityComparator() { + } + /** Code is function of (s, _, ctx, _) */ @Override public int hashCode(ATNConfig o) { diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/AbstractEqualityComparator.java b/runtime/Java/src/org/antlr/v4/runtime/misc/AbstractEqualityComparator.java new file mode 100644 index 000000000..23bb69015 --- /dev/null +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/AbstractEqualityComparator.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.v4.runtime.misc; + +/** + * This abstract base class is provided so performance-critical applications can + * use virtual- instead of interface-dispatch when calling comparator methods. + * + * @author Sam Harwell + */ +public abstract class AbstractEqualityComparator implements EqualityComparator { + +} diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/Array2DHashSet.java b/runtime/Java/src/org/antlr/v4/runtime/misc/Array2DHashSet.java index f82c78713..2df5e7b9e 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/Array2DHashSet.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/Array2DHashSet.java @@ -6,11 +6,14 @@ import java.util.NoSuchElementException; import java.util.Set; /** Set impl with closed hashing (open addressing). */ -public class Array2DHashSet implements EquivalenceSet { +public class Array2DHashSet implements Set { public static final int INITAL_CAPACITY = 16; // must be power of 2 public static final int INITAL_BUCKET_CAPACITY = 8; public static final double LOAD_FACTOR = 0.75; + @NotNull + protected final AbstractEqualityComparator comparator; + protected T[][] buckets; /** How many elements in set */ @@ -22,11 +25,20 @@ public class Array2DHashSet implements EquivalenceSet { protected int initialBucketCapacity = INITAL_BUCKET_CAPACITY; public Array2DHashSet() { - this(INITAL_CAPACITY, INITAL_BUCKET_CAPACITY); + this(null, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY); } - public Array2DHashSet(int initialCapacity, int initialBucketCapacity) { - buckets = (T[][])new Object[initialCapacity][]; + public Array2DHashSet(@Nullable AbstractEqualityComparator comparator) { + this(comparator, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY); + } + + public Array2DHashSet(@Nullable AbstractEqualityComparator comparator, int initialCapacity, int initialBucketCapacity) { + if (comparator == null) { + comparator = ObjectEqualityComparator.INSTANCE; + } + + this.comparator = comparator; + this.buckets = (T[][])new Object[initialCapacity][]; this.initialBucketCapacity = initialBucketCapacity; } @@ -56,7 +68,7 @@ public class Array2DHashSet implements EquivalenceSet { n++; return o; } - if ( equals(existing, o) ) return existing; // found existing, quit + if ( comparator.equals(existing, o) ) return existing; // found existing, quit } // FULL BUCKET, expand and add to end T[] old = bucket; @@ -75,13 +87,13 @@ public class Array2DHashSet implements EquivalenceSet { if ( bucket==null ) return null; // no bucket for (T e : bucket) { if ( e==null ) return null; // empty slot; not there - if ( equals(e, o) ) return e; + if ( comparator.equals(e, o) ) return e; } return null; } protected int getBucket(T o) { - int hash = hashCode(o); + int hash = comparator.hashCode(o); int b = hash & (buckets.length-1); // assumes len is power of 2 return b; } @@ -93,7 +105,7 @@ public class Array2DHashSet implements EquivalenceSet { if ( bucket==null ) continue; for (T o : bucket) { if ( o==null ) break; - h += hashCode(o); + h += comparator.hashCode(o); } } return h; @@ -129,14 +141,6 @@ public class Array2DHashSet implements EquivalenceSet { n = oldSize; } - public int hashCode(T o) { - return o.hashCode(); - } - - public boolean equals(T a, T b) { - return a.equals(b); - } - @Override public boolean add(T t) { T existing = absorb(t); @@ -229,7 +233,7 @@ public class Array2DHashSet implements EquivalenceSet { for (int i=0; i { + + /** + * This method returns a hash code for the specified object. + * + * @param obj The object. + * @return The hash code for {@code obj}. + */ + int hashCode(T obj); + + /** + * This method tests if two objects are equal. + * + * @param a The first object to compare. + * @param b The second object to compare. + * @return {@code true} if {@code a} equals {@code b}, otherwise {@code false}. + */ + boolean equals(T a, T b); + +} diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceMap.java b/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceMap.java deleted file mode 100644 index 2043a0f8e..000000000 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceMap.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.antlr.v4.runtime.misc; - -import java.util.Map; - -public interface EquivalenceMap extends Map, EquivalenceRelation { -} diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceRelation.java b/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceRelation.java deleted file mode 100644 index 66ab24675..000000000 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceRelation.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.antlr.v4.runtime.misc; - -/** What does it mean for two objects to be equivalent? */ -public interface EquivalenceRelation { - public int hashCode(T o); - public boolean equals(T a, T b); -} diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceSet.java b/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceSet.java deleted file mode 100644 index 65954ada4..000000000 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/EquivalenceSet.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.antlr.v4.runtime.misc; - -import java.util.Set; - -/** A set that allows us to override equivalence. For a single set, we might - * want multiple subset perspectives as defined by different hash code - * and equivalence methods. HashSet does not allow us to subclass and - * override the equivalence operations, so we have to implement our own - * sets that are flexible in terms of equivalence. - */ -public interface EquivalenceSet extends Set, EquivalenceRelation { -} diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/FlexibleHashMap.java b/runtime/Java/src/org/antlr/v4/runtime/misc/FlexibleHashMap.java index 657870aa7..407d81728 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/FlexibleHashMap.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/FlexibleHashMap.java @@ -10,7 +10,7 @@ import java.util.Set; /** A limited map (many unsupported operations) that lets me use * varying hashCode/equals. */ -public class FlexibleHashMap implements EquivalenceMap { +public class FlexibleHashMap implements Map { public static final int INITAL_CAPACITY = 16; // must be power of 2 public static final int INITAL_BUCKET_CAPACITY = 8; public static final double LOAD_FACTOR = 0.75; @@ -27,6 +27,9 @@ public class FlexibleHashMap implements EquivalenceMap { } } + @NotNull + protected final AbstractEqualityComparator comparator; + protected LinkedList>[] buckets; /** How many elements in set */ @@ -38,11 +41,20 @@ public class FlexibleHashMap implements EquivalenceMap { protected int initialBucketCapacity = INITAL_BUCKET_CAPACITY; public FlexibleHashMap() { - this(INITAL_CAPACITY, INITAL_BUCKET_CAPACITY); + this(null, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY); } - public FlexibleHashMap(int initialCapacity, int initialBucketCapacity) { - buckets = createEntryListArray(initialBucketCapacity); + public FlexibleHashMap(@Nullable AbstractEqualityComparator comparator) { + this(comparator, INITAL_CAPACITY, INITAL_BUCKET_CAPACITY); + } + + public FlexibleHashMap(@Nullable AbstractEqualityComparator comparator, int initialCapacity, int initialBucketCapacity) { + if (comparator == null) { + comparator = ObjectEqualityComparator.INSTANCE; + } + + this.comparator = comparator; + this.buckets = createEntryListArray(initialBucketCapacity); this.initialBucketCapacity = initialBucketCapacity; } @@ -52,18 +64,8 @@ public class FlexibleHashMap implements EquivalenceMap { return result; } - @Override - public boolean equals(K keyA, K keyB) { - return keyA.equals(keyB); - } - - @Override - public int hashCode(K o) { - return o.hashCode(); - } - protected int getBucket(K key) { - int hash = hashCode(key); + int hash = comparator.hashCode(key); int b = hash & (buckets.length-1); // assumes len is power of 2 return b; } @@ -77,7 +79,9 @@ public class FlexibleHashMap implements EquivalenceMap { LinkedList> bucket = buckets[b]; if ( bucket==null ) return null; // no bucket for (Entry e : bucket) { - if ( equals(e.key, typedKey) ) return e.value; // use special equals + if ( comparator.equals(e.key, typedKey) ) { + return e.value; + } } return null; } @@ -92,7 +96,7 @@ public class FlexibleHashMap implements EquivalenceMap { bucket = buckets[b] = new LinkedList>(); } for (Entry e : bucket) { - if ( equals(e.key, key) ) { + if ( comparator.equals(e.key, key) ) { V prev = e.value; e.value = value; n++; @@ -154,7 +158,7 @@ public class FlexibleHashMap implements EquivalenceMap { if ( bucket==null ) continue; for (Entry e : bucket) { if ( e==null ) break; - h += hashCode(e.key); + h += comparator.hashCode(e.key); } } return h; diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/ObjectEqualityComparator.java b/runtime/Java/src/org/antlr/v4/runtime/misc/ObjectEqualityComparator.java new file mode 100644 index 000000000..d4560c2f3 --- /dev/null +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/ObjectEqualityComparator.java @@ -0,0 +1,74 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.v4.runtime.misc; + +/** + * This default implementation of {@link EqualityComparator} uses object equality + * for comparisons by calling {@link Object#hashCode} and {@link Object#equals}. + * + * @author Sam Harwell + */ +public final class ObjectEqualityComparator extends AbstractEqualityComparator { + public static final ObjectEqualityComparator INSTANCE = new ObjectEqualityComparator(); + + /** + * {@inheritDoc} + *

+ * This implementation returns + * {@code obj.}{@link Object#hashCode hashCode()}. + */ + @Override + public int hashCode(Object obj) { + if (obj == null) { + return 0; + } + + return obj.hashCode(); + } + + /** + * {@inheritDoc} + *

+ * This implementation relies on object equality. If both objects are + * {@code null}, this method returns {@code true}. Otherwise if only + * {@code a} is {@code null}, this method returns {@code false}. Otherwise, + * this method returns the result of + * {@code a.}{@link Object#equals equals}{@code (b)}. + */ + @Override + public boolean equals(Object a, Object b) { + if (a == null) { + return b == null; + } + + return a.equals(b); + } + +}