Skip to content

Commit 6aaaa0a

Browse files
puredangerstuarthalloway
authored andcommitted
CLJ-1336 - make hashing mixing function available in Clojure for external collections. Add generative tests that check external hashing facilities match internal hashes.
Signed-off-by: Stuart Halloway <[email protected]>
1 parent 9bd8d65 commit 6aaaa0a

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

src/clj/clojure/core.clj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4694,6 +4694,16 @@
46944694
:static true}
46954695
[x] (. clojure.lang.Util (hasheq x)))
46964696

4697+
(defn mix-collection-hash
4698+
"Mix final collection hash for ordered or unordered collections.
4699+
hash-basis is the combined collection hash, count is the number
4700+
of elements included in the basis. Note this is the hash code
4701+
consistent with =, different from .hashCode.
4702+
See http://clojure.org/data_structures#hash for full algorithms."
4703+
{:added "1.6"
4704+
:static true}
4705+
[^long hash-basis count] (clojure.lang.Murmur3/mixCollHash hash-basis count))
4706+
46974707
(defn interpose
46984708
"Returns a lazy seq of the elements of coll separated by sep"
46994709
{:added "1.0"

src/jvm/clojure/lang/Murmur3.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public static int hashUnencodedChars(CharSequence input){
9191
return fmix(h1, 2 * input.length());
9292
}
9393

94-
public static int fixCollHash(int hash, int count){
94+
public static int mixCollHash(int hash, int count){
9595
int h1 = seed;
9696
int k1 = mixK1(hash);
9797
h1 = mixH1(h1, k1);
@@ -108,7 +108,7 @@ public static int hashOrdered(Iterable xs){
108108
++n;
109109
}
110110

111-
return fixCollHash(hash, n);
111+
return mixCollHash(hash, n);
112112
}
113113

114114
public static int hashUnordered(Iterable xs){
@@ -120,7 +120,7 @@ public static int hashUnordered(Iterable xs){
120120
++n;
121121
}
122122

123-
return fixCollHash(hash, n);
123+
return mixCollHash(hash, n);
124124
}
125125

126126
private static int mixK1(int k1){
@@ -148,4 +148,4 @@ private static int fmix(int h1, int length){
148148
return h1;
149149
}
150150

151-
}
151+
}

test/clojure/test_clojure/data_structures.clj

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,3 +1074,38 @@
10741074
"Banana" "like", "apple" "love", "7th" "indifferent") ]]
10751075
(doseq [m1 maps1, m2 maps1]
10761076
(is-same-collection m1 m2))))
1077+
1078+
;; *** Collection hashes ***
1079+
;; See: http://clojure.org/data_structures#hash
1080+
1081+
(defn hash-ordered [collection]
1082+
(-> (reduce (fn [acc e] (unchecked-add-int (unchecked-multiply-int 31 acc) (hash e)))
1083+
1
1084+
collection)
1085+
(mix-collection-hash (count collection))))
1086+
1087+
(defn hash-unordered [collection]
1088+
(-> (reduce unchecked-add-int 0 (map hash collection))
1089+
(mix-collection-hash (count collection))))
1090+
1091+
(defn gen-elements
1092+
[]
1093+
(gen/vec gen/anything))
1094+
1095+
(defspec ordered-collection-hashes-match
1096+
identity
1097+
[^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
1098+
(let [v (vec elem)
1099+
l (apply list elem)]
1100+
(is (= (hash v)
1101+
(hash l)
1102+
(hash (map identity elem))
1103+
(hash-ordered elem)))))
1104+
1105+
(defspec unordered-set-hashes-match
1106+
identity
1107+
[^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
1108+
(let [unique-elem (distinct elem)
1109+
s (into #{} unique-elem)]
1110+
(is (= (hash s)
1111+
(hash-unordered unique-elem)))))

0 commit comments

Comments
 (0)