From 7ec0dbc4eb1c449ac1e3dc0a9a486584904ae590 Mon Sep 17 00:00:00 2001 From: Brian Hotopp Date: Fri, 22 May 2026 11:05:47 -0400 Subject: [PATCH] [2.x] fix: Drop net.openhft farmNa, use MurmurHash3 in HashUtil `HashUtil.farmHash` and `BootServerSocket.socketLocation` called `LongHashFunction.farmNa()` from zero-allocation-hashing, whose impl uses `sun.misc.Unsafe` and triggers the terminally-deprecated warning on JDK 23+. Replaced with two `scala.util.hashing.MurmurHash3` calls composed into a 64-bit Long. Matches @eed3si9n's suggestion in the issue thread. The `farmHash` method name and `"farm64-"` cache-string prefix are kept for source compatibility. Existing on-disk caches keyed on `farm64-` see a one-time cold rebuild after upgrade. Fixes sbt/sbt#8073. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../java/sbt/internal/BootServerSocket.java | 3 +-- notes/2.0.0/internal-hash-murmur3.md | 18 ++++++++++++++++++ .../src/main/scala/sbt/util/HashUtil.scala | 6 ++++-- .../src/test/scala/sbt/util/HasherTest.scala | 6 +++--- 4 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 notes/2.0.0/internal-hash-murmur3.md diff --git a/main-command/src/main/java/sbt/internal/BootServerSocket.java b/main-command/src/main/java/sbt/internal/BootServerSocket.java index 744b5116e..78d9d82e4 100644 --- a/main-command/src/main/java/sbt/internal/BootServerSocket.java +++ b/main-command/src/main/java/sbt/internal/BootServerSocket.java @@ -28,7 +28,6 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import net.openhft.hashing.LongHashFunction; import org.scalasbt.ipcsocket.UnixDomainServerSocket; import org.scalasbt.ipcsocket.Win32NamedPipeServerSocket; import org.scalasbt.ipcsocket.Win32SecurityLevel; @@ -303,7 +302,7 @@ public class BootServerSocket implements AutoCloseable { public static String socketLocation(final Path base) throws UnsupportedEncodingException, IOException { final Path target = base.resolve("project").resolve("target"); - long hash = LongHashFunction.farmNa().hashBytes(target.toString().getBytes("UTF-8")); + long hash = sbt.util.HashUtil.farmHash(target.toString().getBytes("UTF-8")); if (isWindows) { return "sbt-load" + hash; } else { diff --git a/notes/2.0.0/internal-hash-murmur3.md b/notes/2.0.0/internal-hash-murmur3.md new file mode 100644 index 000000000..342e938a1 --- /dev/null +++ b/notes/2.0.0/internal-hash-murmur3.md @@ -0,0 +1,18 @@ +## Internal hash function: FarmHash → MurmurHash3 + +The internal `farmHash` used for cache-key derivation +(`HashUtil.farmHash`, `BootServerSocket.socketLocation`) is now +implemented as two `scala.util.hashing.MurmurHash3` calls composed +into a 64-bit `Long`. Previously it called +`net.openhft.hashing.LongHashFunction.farmNa()`, whose impl uses +`sun.misc.Unsafe` and triggers the terminally-deprecated warning +under JDK 23+. + +The `"farm64-"` cache-string prefix is retained for source +compatibility. Existing on-disk caches keyed on `farm64-` will +see a one-time cold rebuild after upgrade — no data loss; first +build after upgrade recomputes the hash on touched inputs. + +Fixes [#8073][i8073]. + + [i8073]: https://github.com/sbt/sbt/issues/8073 diff --git a/util-cache/src/main/scala/sbt/util/HashUtil.scala b/util-cache/src/main/scala/sbt/util/HashUtil.scala index 3a5f976ea..9cd95aca1 100644 --- a/util-cache/src/main/scala/sbt/util/HashUtil.scala +++ b/util-cache/src/main/scala/sbt/util/HashUtil.scala @@ -1,11 +1,13 @@ package sbt.util import java.nio.file.{ Files, Path } -import net.openhft.hashing.LongHashFunction +import scala.util.hashing.MurmurHash3 object HashUtil: private[sbt] def farmHash(bytes: Array[Byte]): Long = - LongHashFunction.farmNa().hashBytes(bytes) + val hi = MurmurHash3.bytesHash(bytes, 0x9747b28c) + val lo = MurmurHash3.bytesHash(bytes, 0x85ebca6b) + (hi.toLong << 32) | (lo.toLong & 0xffffffffL) private[sbt] def farmHash(path: Path): Long = import sbt.io.Hash diff --git a/util-cache/src/test/scala/sbt/util/HasherTest.scala b/util-cache/src/test/scala/sbt/util/HasherTest.scala index ad881a43d..1e86a978c 100644 --- a/util-cache/src/test/scala/sbt/util/HasherTest.scala +++ b/util-cache/src/test/scala/sbt/util/HasherTest.scala @@ -8,9 +8,9 @@ import xsbti.HashedVirtualFileRef object HasherTest extends BasicTestSuite: - final val blankContentHash = -7286425919675154353L - val blankContentHashStr = "farm64-9ae16a3b2f90404f" - final val blankATxtHash = 1166939303L + final val blankContentHash = -1461767548567390449L + val blankContentHashStr = "farm64-ebb6c228cb72770f" + final val blankATxtHash = -1644914753 test("The IntJsonFormat should convert an Int to an int hash") { import BasicJsonProtocol.given