Skip to content

Commit c42e325

Browse files
Merge pull request #4865 from dotty-staging/fix-tasty-hash
Fix tasty hash
2 parents 416d85c + ac7647f commit c42e325

File tree

3 files changed

+63
-22
lines changed

3 files changed

+63
-22
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dotty.tools.dotc.core.tasty
2+
3+
object TastyHash {
4+
5+
/** Returns a non-cryptographic 64-bit hash of the array.
6+
*
7+
* from https://en.wikipedia.org/wiki/PJW_hash_function#Algorithm
8+
*/
9+
def pjwHash64(data: Array[Byte]): Long = {
10+
var h = 0L
11+
var i = 0
12+
while (i < data.length) {
13+
val d = data(i) & 0xFFL // Interpret byte as unsigned byte
14+
h = (h << 8) + d
15+
val high = h & 0xFF00000000000000L
16+
h ^= high >>> 48L
17+
h &= ~high
18+
i += 1
19+
}
20+
h
21+
}
22+
23+
}

compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@ class TastyPickler(val rootCls: ClassSymbol) {
2020
sections += ((nameBuffer.nameIndex(name.toTermName), buf))
2121

2222
def assembleParts(): Array[Byte] = {
23-
def lengthWithLength(buf: TastyBuffer) = {
24-
buf.assemble()
23+
def lengthWithLength(buf: TastyBuffer) =
2524
buf.length + natSize(buf.length)
26-
}
2725

28-
val uuidLow: Long = pjwHash64(nameBuffer.bytes)
29-
val uuidHi: Long = sections.iterator.map(x => pjwHash64(x._2.bytes)).fold(0L)(_ ^ _)
26+
nameBuffer.assemble()
27+
sections.foreach(_._2.assemble())
28+
29+
val nameBufferHash = TastyHash.pjwHash64(nameBuffer.bytes)
30+
val treeSectionHash +: otherSectionHashes = sections.map(x => TastyHash.pjwHash64(x._2.bytes))
31+
32+
// Hash of name table and tree
33+
val uuidLow: Long = nameBufferHash ^ treeSectionHash
34+
// Hash of positions, comments and any additional section
35+
val uuidHi: Long = otherSectionHashes.fold(0L)(_ ^ _)
3036

3137
val headerBuffer = {
3238
val buf = new TastyBuffer(header.length + 24)
@@ -72,21 +78,4 @@ class TastyPickler(val rootCls: ClassSymbol) {
7278

7379
val treePkl = new TreePickler(this)
7480

75-
/** Returns a non-cryptographic 64-bit hash of the array.
76-
*
77-
* from https://en.wikipedia.org/wiki/PJW_hash_function#Implementation
78-
*/
79-
private def pjwHash64(data: Array[Byte]): Long = {
80-
var h = 0L
81-
var high = 0L
82-
var i = 0
83-
while (i < data.length) {
84-
h = (h << 4) + data(i)
85-
high = h & 0xF0000000L
86-
h ^= high >> 24
87-
h &= ~high
88-
i += 1
89-
}
90-
h
91-
}
9281
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dotty.tools.dotc
2+
3+
import org.junit.Test
4+
import org.junit.Assert.assertEquals
5+
6+
import dotty.tools.dotc.core.tasty.TastyHash.pjwHash64
7+
8+
class TastyHashTest {
9+
@Test def pjwHash64Tests(): Unit = {
10+
testHash(0L, Array.empty)
11+
testHash(0L, Array(0))
12+
testHash(1L, Array(1))
13+
testHash(0x7fL, Array(Byte.MaxValue))
14+
testHash(0x80L, Array(Byte.MinValue))
15+
testHash(0x101L, Array(1, 1))
16+
testHash(0X10101L, Array(1, 1, 1))
17+
testHash(0X1010101L, Array(1, 1, 1, 1))
18+
testHash(0X101010101L, Array(1, 1, 1, 1, 1))
19+
testHash(0X202020202L, Array(2, 2, 2, 2, 2))
20+
testHash(0X1010101L, Array.fill(1024)(1))
21+
testHash(0xaafefeaaab54ffL, Array.tabulate(1024)(_.toByte))
22+
testHash(0x34545c16020230L, "abcdefghijklmnopqrstuvwxyz1234567890".getBytes)
23+
}
24+
25+
def testHash(expected: Long, arr: Array[Byte]): Unit = {
26+
assertEquals(s"0x${expected.toHexString}L", s"0x${pjwHash64(arr).toHexString}L")
27+
}
28+
29+
}

0 commit comments

Comments
 (0)