diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyHash.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyHash.scala new file mode 100644 index 000000000000..6845f36fcf06 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyHash.scala @@ -0,0 +1,23 @@ +package dotty.tools.dotc.core.tasty + +object TastyHash { + + /** Returns a non-cryptographic 64-bit hash of the array. + * + * from https://en.wikipedia.org/wiki/PJW_hash_function#Algorithm + */ + def pjwHash64(data: Array[Byte]): Long = { + var h = 0L + var i = 0 + while (i < data.length) { + val d = data(i) & 0xFFL // Interpret byte as unsigned byte + h = (h << 8) + d + val high = h & 0xFF00000000000000L + h ^= high >>> 48L + h &= ~high + i += 1 + } + h + } + +} diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala index a15c8c861fec..d7e1ddf0d42f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -20,13 +20,19 @@ class TastyPickler(val rootCls: ClassSymbol) { sections += ((nameBuffer.nameIndex(name.toTermName), buf)) def assembleParts(): Array[Byte] = { - def lengthWithLength(buf: TastyBuffer) = { - buf.assemble() + def lengthWithLength(buf: TastyBuffer) = buf.length + natSize(buf.length) - } - val uuidLow: Long = pjwHash64(nameBuffer.bytes) - val uuidHi: Long = sections.iterator.map(x => pjwHash64(x._2.bytes)).fold(0L)(_ ^ _) + nameBuffer.assemble() + sections.foreach(_._2.assemble()) + + val nameBufferHash = TastyHash.pjwHash64(nameBuffer.bytes) + val treeSectionHash +: otherSectionHashes = sections.map(x => TastyHash.pjwHash64(x._2.bytes)) + + // Hash of name table and tree + val uuidLow: Long = nameBufferHash ^ treeSectionHash + // Hash of positions, comments and any additional section + val uuidHi: Long = otherSectionHashes.fold(0L)(_ ^ _) val headerBuffer = { val buf = new TastyBuffer(header.length + 24) @@ -72,21 +78,4 @@ class TastyPickler(val rootCls: ClassSymbol) { val treePkl = new TreePickler(this) - /** Returns a non-cryptographic 64-bit hash of the array. - * - * from https://en.wikipedia.org/wiki/PJW_hash_function#Implementation - */ - private def pjwHash64(data: Array[Byte]): Long = { - var h = 0L - var high = 0L - var i = 0 - while (i < data.length) { - h = (h << 4) + data(i) - high = h & 0xF0000000L - h ^= high >> 24 - h &= ~high - i += 1 - } - h - } } diff --git a/compiler/test/dotty/tools/dotc/TastyHashTest.scala b/compiler/test/dotty/tools/dotc/TastyHashTest.scala new file mode 100644 index 000000000000..b8cb5c9c0bc2 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/TastyHashTest.scala @@ -0,0 +1,29 @@ +package dotty.tools.dotc + +import org.junit.Test +import org.junit.Assert.assertEquals + +import dotty.tools.dotc.core.tasty.TastyHash.pjwHash64 + +class TastyHashTest { + @Test def pjwHash64Tests(): Unit = { + testHash(0L, Array.empty) + testHash(0L, Array(0)) + testHash(1L, Array(1)) + testHash(0x7fL, Array(Byte.MaxValue)) + testHash(0x80L, Array(Byte.MinValue)) + testHash(0x101L, Array(1, 1)) + testHash(0X10101L, Array(1, 1, 1)) + testHash(0X1010101L, Array(1, 1, 1, 1)) + testHash(0X101010101L, Array(1, 1, 1, 1, 1)) + testHash(0X202020202L, Array(2, 2, 2, 2, 2)) + testHash(0X1010101L, Array.fill(1024)(1)) + testHash(0xaafefeaaab54ffL, Array.tabulate(1024)(_.toByte)) + testHash(0x34545c16020230L, "abcdefghijklmnopqrstuvwxyz1234567890".getBytes) + } + + def testHash(expected: Long, arr: Array[Byte]): Unit = { + assertEquals(s"0x${expected.toHexString}L", s"0x${pjwHash64(arr).toHexString}L") + } + +}