Skip to content

Commit

Permalink
use hash consistent with equiv
Browse files Browse the repository at this point in the history
  • Loading branch information
richhickey committed Oct 23, 2011
1 parent f006a81 commit b5f5ba2
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 39 deletions.
26 changes: 7 additions & 19 deletions clojure.iml
@@ -1,29 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module relativePaths="true" type="JAVA_MODULE" version="4"> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="false">
<facet type="Clojure" name="Clojure"> <output url="file://$MODULE_DIR$/target/classes" />
<configuration /> <output-test url="file://$MODULE_DIR$/target/test-classes" />
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/resources" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/clj" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/clj" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/jvm" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/jvm" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="asm-3.0" level="project" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$APPLICATION_HOME_DIR$/lib/javaee.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component> </component>
</module> </module>


15 changes: 13 additions & 2 deletions src/jvm/clojure/lang/APersistentMap.java
Expand Up @@ -13,7 +13,7 @@
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;


public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence { public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence, IHashEq {
int _hash = -1; int _hash = -1;


public String toString(){ public String toString(){
Expand Down Expand Up @@ -54,7 +54,7 @@ static public boolean mapEquals(IPersistentMap m1, Object obj){
return false; return false;
Map m = (Map) obj; Map m = (Map) obj;


if(m.size() != m1.count() || m.hashCode() != m1.hashCode()) if(m.size() != m1.count())
return false; return false;


for(ISeq s = m1.seq(); s != null; s = s.next()) for(ISeq s = m1.seq(); s != null; s = s.next())
Expand Down Expand Up @@ -110,6 +110,17 @@ static public int mapHash(IPersistentMap m){
return hash; return hash;
} }


public int hasheq(){
int hash = 0;
for(ISeq s = this.seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
hash += Util.hasheq(e.getKey()) ^
Util.hasheq(e.getValue());
}
return hash;
}

static public class KeySeq extends ASeq{ static public class KeySeq extends ASeq{
ISeq seq; ISeq seq;


Expand Down
12 changes: 11 additions & 1 deletion src/jvm/clojure/lang/APersistentSet.java
Expand Up @@ -17,7 +17,7 @@
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;


public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable { public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable, IHashEq {
int _hash = -1; int _hash = -1;
final IPersistentMap impl; final IPersistentMap impl;


Expand Down Expand Up @@ -92,6 +92,16 @@ public int hashCode(){
return _hash; return _hash;
} }


public int hasheq(){
int hash = 0;
for(ISeq s = seq(); s != null; s = s.next())
{
Object e = s.first();
hash += Util.hasheq(e);
}
return hash;
}

public Object[] toArray(){ public Object[] toArray(){
return RT.seqToArray(seq()); return RT.seqToArray(seq());
} }
Expand Down
13 changes: 12 additions & 1 deletion src/jvm/clojure/lang/APersistentVector.java
Expand Up @@ -18,7 +18,7 @@
public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable, public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable,
List, List,
RandomAccess, Comparable, RandomAccess, Comparable,
Serializable { Serializable, IHashEq {
int _hash = -1; int _hash = -1;


public String toString(){ public String toString(){
Expand Down Expand Up @@ -152,6 +152,17 @@ public int hashCode(){
return _hash; return _hash;
} }


public int hasheq(){
int hash = 1;
Iterator i = iterator();
while(i.hasNext())
{
Object obj = i.next();
hash = 31 * hash + Util.hasheq(obj);
}
return hash;
}

public Object get(int index){ public Object get(int index){
return nth(index); return nth(index);
} }
Expand Down
11 changes: 10 additions & 1 deletion src/jvm/clojure/lang/ASeq.java
Expand Up @@ -13,7 +13,7 @@
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;


public abstract class ASeq extends Obj implements ISeq, Sequential, List, Serializable { public abstract class ASeq extends Obj implements ISeq, Sequential, List, Serializable, IHashEq {
transient int _hash = -1; transient int _hash = -1;


public String toString(){ public String toString(){
Expand Down Expand Up @@ -73,6 +73,15 @@ public int hashCode(){
return _hash; return _hash;
} }


public int hasheq(){
int hash = 1;
for(ISeq s = seq(); s != null; s = s.next())
{
hash = 31 * hash + Util.hasheq(s.first());
}
return hash;
}



//public Object reduce(IFn f) { //public Object reduce(IFn f) {
// Object ret = first(); // Object ret = first();
Expand Down
17 changes: 17 additions & 0 deletions src/jvm/clojure/lang/IHashEq.java
@@ -0,0 +1,17 @@
/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/

/* rich 10/23/11 */

package clojure.lang;

public interface IHashEq{
int hasheq();
}
9 changes: 8 additions & 1 deletion src/jvm/clojure/lang/LazySeq.java
Expand Up @@ -14,7 +14,7 @@


import java.util.*; import java.util.*;


public final class LazySeq extends Obj implements ISeq, Sequential, List, IPending{ public final class LazySeq extends Obj implements ISeq, Sequential, List, IPending, IHashEq{


private IFn fn; private IFn fn;
private Object sv; private Object sv;
Expand Down Expand Up @@ -118,6 +118,13 @@ public int hashCode(){
return Util.hash(seq()); return Util.hash(seq());
} }


public int hasheq(){
ISeq s = seq();
if(s == null)
return 1;
return Util.hasheq(seq());
}

public boolean equals(Object o){ public boolean equals(Object o){
ISeq s = seq(); ISeq s = seq();
if(s != null) if(s != null)
Expand Down
18 changes: 16 additions & 2 deletions src/jvm/clojure/lang/Numbers.java
Expand Up @@ -941,11 +941,11 @@ static public enum Category {INTEGER, FLOATING, DECIMAL, RATIO};
static Ops ops(Object x){ static Ops ops(Object x){
Class xc = x.getClass(); Class xc = x.getClass();


if(xc == Integer.class) if(xc == Long.class)
return LONG_OPS; return LONG_OPS;
else if(xc == Double.class) else if(xc == Double.class)
return DOUBLE_OPS; return DOUBLE_OPS;
else if(xc == Long.class) else if(xc == Integer.class)
return LONG_OPS; return LONG_OPS;
else if(xc == Float.class) else if(xc == Float.class)
return DOUBLE_OPS; return DOUBLE_OPS;
Expand All @@ -961,6 +961,20 @@ else if(xc == BigDecimal.class)
return LONG_OPS; return LONG_OPS;
} }


static int hasheq(Number x){
Class xc = x.getClass();

if(xc == Long.class
|| xc == Integer.class
|| xc == Short.class
|| xc == Byte.class)
{
long lpart = x.longValue();
return (int) (lpart ^ (lpart >>> 32));
}
return x.hashCode();
}

static Category category(Object x){ static Category category(Object x){
Class xc = x.getClass(); Class xc = x.getClass();


Expand Down
28 changes: 16 additions & 12 deletions src/jvm/clojure/lang/PersistentHashMap.java
Expand Up @@ -117,16 +117,20 @@ public PersistentHashMap(IPersistentMap meta, int count, INode root, boolean has
this.nullValue = nullValue; this.nullValue = nullValue;
} }


static int hash(Object k){
return Util.hasheq(k);
}

public boolean containsKey(Object key){ public boolean containsKey(Object key){
if(key == null) if(key == null)
return hasNull; return hasNull;
return (root != null) ? root.find(0, Util.hash(key), key, NOT_FOUND) != NOT_FOUND : false; return (root != null) ? root.find(0, hash(key), key, NOT_FOUND) != NOT_FOUND : false;
} }


public IMapEntry entryAt(Object key){ public IMapEntry entryAt(Object key){
if(key == null) if(key == null)
return hasNull ? new MapEntry(null, nullValue) : null; return hasNull ? new MapEntry(null, nullValue) : null;
return (root != null) ? root.find(0, Util.hash(key), key) : null; return (root != null) ? root.find(0, hash(key), key) : null;
} }


public IPersistentMap assoc(Object key, Object val){ public IPersistentMap assoc(Object key, Object val){
Expand All @@ -137,7 +141,7 @@ public IPersistentMap assoc(Object key, Object val){
} }
Box addedLeaf = new Box(null); Box addedLeaf = new Box(null);
INode newroot = (root == null ? BitmapIndexedNode.EMPTY : root) INode newroot = (root == null ? BitmapIndexedNode.EMPTY : root)
.assoc(0, Util.hash(key), key, val, addedLeaf); .assoc(0, hash(key), key, val, addedLeaf);
if(newroot == root) if(newroot == root)
return this; return this;
return new PersistentHashMap(meta(), addedLeaf.val == null ? count : count + 1, newroot, hasNull, nullValue); return new PersistentHashMap(meta(), addedLeaf.val == null ? count : count + 1, newroot, hasNull, nullValue);
Expand All @@ -146,7 +150,7 @@ public IPersistentMap assoc(Object key, Object val){
public Object valAt(Object key, Object notFound){ public Object valAt(Object key, Object notFound){
if(key == null) if(key == null)
return hasNull ? nullValue : notFound; return hasNull ? nullValue : notFound;
return root != null ? root.find(0, Util.hash(key), key, notFound) : notFound; return root != null ? root.find(0, hash(key), key, notFound) : notFound;
} }


public Object valAt(Object key){ public Object valAt(Object key){
Expand All @@ -164,7 +168,7 @@ public IPersistentMap without(Object key){
return hasNull ? new PersistentHashMap(meta(), count - 1, root, false, null) : this; return hasNull ? new PersistentHashMap(meta(), count - 1, root, false, null) : this;
if(root == null) if(root == null)
return this; return this;
INode newroot = root.without(0, Util.hash(key), key); INode newroot = root.without(0, hash(key), key);
if(newroot == root) if(newroot == root)
return this; return this;
return new PersistentHashMap(meta(), count - 1, newroot, hasNull, nullValue); return new PersistentHashMap(meta(), count - 1, newroot, hasNull, nullValue);
Expand Down Expand Up @@ -238,7 +242,7 @@ ITransientMap doAssoc(Object key, Object val) {
// Box leafFlag = new Box(null); // Box leafFlag = new Box(null);
leafFlag.val = null; leafFlag.val = null;
INode n = (root == null ? BitmapIndexedNode.EMPTY : root) INode n = (root == null ? BitmapIndexedNode.EMPTY : root)
.assoc(edit, 0, Util.hash(key), key, val, leafFlag); .assoc(edit, 0, hash(key), key, val, leafFlag);
if (n != this.root) if (n != this.root)
this.root = n; this.root = n;
if(leafFlag.val != null) this.count++; if(leafFlag.val != null) this.count++;
Expand All @@ -256,7 +260,7 @@ ITransientMap doWithout(Object key) {
if (root == null) return this; if (root == null) return this;
// Box leafFlag = new Box(null); // Box leafFlag = new Box(null);
leafFlag.val = null; leafFlag.val = null;
INode n = root.without(edit, 0, Util.hash(key), key, leafFlag); INode n = root.without(edit, 0, hash(key), key, leafFlag);
if (n != root) if (n != root)
this.root = n; this.root = n;
if(leafFlag.val != null) this.count--; if(leafFlag.val != null) this.count--;
Expand All @@ -276,7 +280,7 @@ Object doValAt(Object key, Object notFound) {
return notFound; return notFound;
if (root == null) if (root == null)
return null; return null;
return root.find(0, Util.hash(key), key, notFound); return root.find(0, hash(key), key, notFound);
} }


int doCount() { int doCount() {
Expand Down Expand Up @@ -525,7 +529,7 @@ public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){
if (array[j] == null) if (array[j] == null)
nodes[i] = (INode) array[j+1]; nodes[i] = (INode) array[j+1];
else else
nodes[i] = EMPTY.assoc(shift + 5, Util.hash(array[j]), array[j], array[j+1], addedLeaf); nodes[i] = EMPTY.assoc(shift + 5, hash(array[j]), array[j], array[j+1], addedLeaf);
j += 2; j += 2;
} }
return new ArrayNode(null, n + 1, nodes); return new ArrayNode(null, n + 1, nodes);
Expand Down Expand Up @@ -670,7 +674,7 @@ public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key
if (array[j] == null) if (array[j] == null)
nodes[i] = (INode) array[j+1]; nodes[i] = (INode) array[j+1];
else else
nodes[i] = EMPTY.assoc(edit, shift + 5, Util.hash(array[j]), array[j], array[j+1], addedLeaf); nodes[i] = EMPTY.assoc(edit, shift + 5, hash(array[j]), array[j], array[j+1], addedLeaf);
j += 2; j += 2;
} }
return new ArrayNode(edit, n + 1, nodes); return new ArrayNode(edit, n + 1, nodes);
Expand Down Expand Up @@ -976,7 +980,7 @@ private static Object[] removePair(Object[] array, int i) {
} }


private static INode createNode(int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) { private static INode createNode(int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) {
int key1hash = Util.hash(key1); int key1hash = hash(key1);
if(key1hash == key2hash) if(key1hash == key2hash)
return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2}); return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
Box _ = new Box(null); Box _ = new Box(null);
Expand All @@ -987,7 +991,7 @@ private static INode createNode(int shift, Object key1, Object val1, int key2has
} }


private static INode createNode(AtomicReference<Thread> edit, int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) { private static INode createNode(AtomicReference<Thread> edit, int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) {
int key1hash = Util.hash(key1); int key1hash = hash(key1);
if(key1hash == key2hash) if(key1hash == key2hash)
return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2}); return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
Box _ = new Box(null); Box _ = new Box(null);
Expand Down
10 changes: 10 additions & 0 deletions src/jvm/clojure/lang/Util.java
Expand Up @@ -116,6 +116,16 @@ static public int hash(Object o){
return o.hashCode(); return o.hashCode();
} }


static public int hasheq(Object o){
if(o == null)
return 0;
if(o instanceof Number)
return Numbers.hasheq((Number)o);
else if(o instanceof IHashEq)
return ((IHashEq)o).hasheq();
return o.hashCode();
}

static public int hashCombine(int seed, int hash){ static public int hashCombine(int seed, int hash){
//a la boost //a la boost
seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
Expand Down

0 comments on commit b5f5ba2

Please sign in to comment.