diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java index 0e5b3ddfa..95af660e5 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/ATNConfig.java @@ -31,6 +31,7 @@ package org.antlr.v4.runtime.atn; import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.misc.MurmurHash; import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.misc.Nullable; @@ -159,13 +160,14 @@ public class ATNConfig { @Override public int hashCode() { - int hashCode = 7; - hashCode = 5 * hashCode + state.stateNumber; - hashCode = 5 * hashCode + alt; - hashCode = 5 * hashCode + (context != null ? context.hashCode() : 0); - hashCode = 5 * hashCode + semanticContext.hashCode(); - return hashCode; - } + int hashCode = MurmurHash.initialize(7); + hashCode = MurmurHash.update(hashCode, state.stateNumber); + hashCode = MurmurHash.update(hashCode, alt); + hashCode = MurmurHash.update(hashCode, context); + hashCode = MurmurHash.update(hashCode, semanticContext); + hashCode = MurmurHash.finish(hashCode, 4); + return hashCode; + } @Override public String toString() { diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/ArrayPredictionContext.java b/runtime/Java/src/org/antlr/v4/runtime/atn/ArrayPredictionContext.java index 0d1835649..60dd4303d 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/ArrayPredictionContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/ArrayPredictionContext.java @@ -58,49 +58,6 @@ public class ArrayPredictionContext extends PredictionContext { this.returnStates = returnStates; } -//ArrayPredictionContext(@NotNull PredictionContext[] parents, int[] returnStates, int parentHashCode, int returnStateHashCode) { -// super(calculateHashCode(parentHashCode, returnStateHashCode)); -// assert parents.length == returnStates.length; -// assert returnStates.length > 1 || returnStates[0] != EMPTY_FULL_STATE_KEY : "Should be using PredictionContext.EMPTY instead."; -// -// this.parents = parents; -// this.returnStates = returnStates; -// } -// -//ArrayPredictionContext(@NotNull PredictionContext[] parents, int[] returnStates, int hashCode) { -// super(hashCode); -// assert parents.length == returnStates.length; -// assert returnStates.length > 1 || returnStates[0] != EMPTY_FULL_STATE_KEY : "Should be using PredictionContext.EMPTY instead."; -// -// this.parents = parents; -// this.returnStates = returnStates; -// } - - protected static int calculateHashCode(PredictionContext[] parents, int[] returnStates) { - return calculateHashCode(calculateParentHashCode(parents), - calculateReturnStatesHashCode(returnStates)); - } - - protected static int calculateParentHashCode(PredictionContext[] parents) { - int hashCode = 1; - for (PredictionContext p : parents) { - if ( p!=null ) { // can be null for full ctx stack in ArrayPredictionContext - hashCode = hashCode * 31 ^ p.hashCode(); - } - } - - return hashCode; - } - - protected static int calculateReturnStatesHashCode(int[] returnStates) { - int hashCode = 1; - for (int state : returnStates) { - hashCode = hashCode * 31 ^ state; - } - - return hashCode; - } - @Override public Iterator iterator() { return new Iterator() { diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java index 368eebc7c..8cd79cabf 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/LexerATNConfig.java @@ -30,6 +30,7 @@ package org.antlr.v4.runtime.atn; +import org.antlr.v4.runtime.misc.MurmurHash; import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.misc.Nullable; @@ -84,8 +85,13 @@ public class LexerATNConfig extends ATNConfig { @Override public int hashCode() { - int hashCode = super.hashCode(); - hashCode = 35 * hashCode ^ (passedThroughNonGreedyDecision ? 1 : 0); + int hashCode = MurmurHash.initialize(7); + hashCode = MurmurHash.update(hashCode, state.stateNumber); + hashCode = MurmurHash.update(hashCode, alt); + hashCode = MurmurHash.update(hashCode, context); + hashCode = MurmurHash.update(hashCode, semanticContext); + hashCode = MurmurHash.update(hashCode, passedThroughNonGreedyDecision ? 1 : 0); + hashCode = MurmurHash.finish(hashCode, 5); return hashCode; } diff --git a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java index b8b7a7d3e..c93088b5f 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java +++ b/runtime/Java/src/org/antlr/v4/runtime/atn/PredictionContext.java @@ -33,6 +33,7 @@ package org.antlr.v4.runtime.atn; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.misc.DoubleKeyMap; +import org.antlr.v4.runtime.misc.MurmurHash; import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.misc.Nullable; @@ -61,9 +62,32 @@ public abstract class PredictionContext implements Iterable + * private int referenceHashCode() { + * int hash = {@link MurmurHash#initialize}({@link #INITIAL_HASH}); + * + * for (int i = 0; i < {@link #size()}; i++) { + * hash = {@link MurmurHash#update}(hash, {@link #getParent}(i)); + * } + * + * for (int i = 0; i < {@link #size()}; i++) { + * hash = {@link MurmurHash#update}(hash, {@link #getReturnState}(i)); + * } + * + * hash = {@link MurmurHash#finish}(hash, 2 * {@link #size()}); + * return hash; + * } + * + */ public final int cachedHashCode; protected PredictionContext(int cachedHashCode) { @@ -117,12 +141,40 @@ public abstract class PredictionContext implements Iterable implements Set { @Override public int hashCode() { - int h = 0; + int hash = MurmurHash.initialize(); for (T[] bucket : buckets) { if ( bucket==null ) continue; for (T o : bucket) { if ( o==null ) break; - h += comparator.hashCode(o); + hash = MurmurHash.update(hash, comparator.hashCode(o)); } } - return h; + + hash = MurmurHash.finish(hash, size()); + return hash; } @Override 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 ae24920ec..7f9e86898 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/FlexibleHashMap.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/FlexibleHashMap.java @@ -183,15 +183,17 @@ public class FlexibleHashMap implements Map { @Override public int hashCode() { - int h = 0; + int hash = MurmurHash.initialize(); for (LinkedList> bucket : buckets) { if ( bucket==null ) continue; for (Entry e : bucket) { if ( e==null ) break; - h += comparator.hashCode(e.key); + hash = MurmurHash.update(hash, comparator.hashCode(e.key)); } } - return h; + + hash = MurmurHash.finish(hash, size()); + return hash; } @Override diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java b/runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java index 58a2144b0..007680fe1 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java @@ -424,11 +424,14 @@ public class IntervalSet implements IntSet { @Override public int hashCode() { - if ( isNil() ) return 0; - int n = 0; - // just add left edge of intervals - for (Interval I : intervals) n += I.a; - return n; + int hash = MurmurHash.initialize(); + for (Interval I : intervals) { + hash = MurmurHash.update(hash, I.a); + hash = MurmurHash.update(hash, I.b); + } + + hash = MurmurHash.finish(hash, intervals.size() * 2); + return hash; } /** Are two IntervalSets equal? Because all intervals are sorted diff --git a/runtime/Java/src/org/antlr/v4/runtime/misc/MurmurHash.java b/runtime/Java/src/org/antlr/v4/runtime/misc/MurmurHash.java new file mode 100644 index 000000000..4d5d0ea66 --- /dev/null +++ b/runtime/Java/src/org/antlr/v4/runtime/misc/MurmurHash.java @@ -0,0 +1,137 @@ +/* + * [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; + +/** + * + * @author Sam Harwell + */ +public final class MurmurHash { + + private static final int DEFAULT_SEED = 0; + + /** + * Initialize the hash using the default seed value. + * + * @return the intermediate hash value + */ + public static int initialize() { + return initialize(DEFAULT_SEED); + } + + /** + * Initialize the hash using the specified {@code seed}. + * + * @param seed the seed + * @return the intermediate hash value + */ + public static int initialize(int seed) { + return seed; + } + + /** + * Update the intermediate hash value for the next input {@code value}. + * + * @param hash the intermediate hash value + * @param value the value to add to the current hash + * @return the updated intermediate hash value + */ + public static int update(int hash, int value) { + final int c1 = 0xCC9E2D51; + final int c2 = 0x1B873593; + final int r1 = 15; + final int r2 = 13; + final int m = 5; + final int n = 0xE6546B64; + + int k = value; + k = k * c1; + k = (k << r1) | (k >>> (32 - r1)); + k = k * c2; + + hash = hash ^ k; + hash = (hash << r2) | (hash >>> (32 - r2)); + hash = hash * m + n; + + return hash; + } + + /** + * Update the intermediate hash value for the next input {@code value}. + * + * @param hash the intermediate hash value + * @param value the value to add to the current hash + * @return the updated intermediate hash value + */ + public static int update(int hash, Object value) { + return update(hash, value != null ? value.hashCode() : 0); + } + + /** + * Apply the final computation steps to the intermediate value {@code hash} + * to form the final result of the MurmurHash 3 hash function. + * + * @param hash the intermediate hash value + * @param numberOfWords the number of integer values added to the hash + * @return the final hash result + */ + public static int finish(int hash, int numberOfWords) { + hash = hash ^ (numberOfWords * 4); + hash = hash ^ (hash >>> 16); + hash = hash * 0x85EBCA6B; + hash = hash ^ (hash >>> 13); + hash = hash * 0xC2B2AE35; + hash = hash ^ (hash >>> 16); + return hash; + } + + /** + * Utility function to compute the hash code of an array using the + * MurmurHash algorithm. + * + * @param the array element type + * @param data the array data + * @param seed the seed for the MurmurHash algorithm + * @return the hash code of the data + */ + public static int hashCode(T[] data, int seed) { + int hash = initialize(seed); + for (T value : data) { + hash = update(hash, value); + } + + hash = finish(hash, data.length); + return hash; + } + + private MurmurHash() { + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextGetterDecl.java index 3977e0c16..99ceef298 100644 --- a/tool/src/org/antlr/v4/codegen/model/decl/ContextGetterDecl.java +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextGetterDecl.java @@ -31,6 +31,7 @@ package org.antlr.v4.codegen.model.decl; import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.MurmurHash; public abstract class ContextGetterDecl extends Decl { public ContextGetterDecl(OutputModelFactory factory, String name) { @@ -44,7 +45,11 @@ public abstract class ContextGetterDecl extends Decl { @Override public int hashCode() { - return name.hashCode() + getArgType().hashCode(); + int hash = MurmurHash.initialize(); + hash = MurmurHash.update(hash, name); + hash = MurmurHash.update(hash, getArgType()); + hash = MurmurHash.finish(hash, 2); + return hash; } /** Make sure that a getter does not equal a label. X() and X are ok.