Add BLAKE3 hasher to vfs
This PR adds the Blake3Hasher and Blake3HashFunction classes to vfs and
makes them available under the flag --digest_function=BLAKE3.
This is a partial commit for #18658.
Closes #18784.
PiperOrigin-RevId: 550525978
Change-Id: Iedc0886c51755585d56b4d8f47676d3be5bbedba
diff --git a/BUILD b/BUILD
index 84c15b6..0540bb8 100644
--- a/BUILD
+++ b/BUILD
@@ -96,6 +96,7 @@
pkg_tar(
name = "bootstrap-jars",
srcs = [
+ "@blake3",
"@com_google_protobuf//:protobuf_java",
"@com_google_protobuf//:protobuf_java_util",
"@com_google_protobuf//:protobuf_javalite",
diff --git a/MODULE.bazel b/MODULE.bazel
index 4c14098..4c4d4fe 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -14,6 +14,7 @@
bazel_dep(name = "rules_pkg", version = "0.7.0")
bazel_dep(name = "stardoc", version = "0.5.3", repo_name = "io_bazel_skydoc")
bazel_dep(name = "zstd-jni", version = "1.5.2-3")
+bazel_dep(name = "blake3", version = "1.3.3")
bazel_dep(name = "zlib", version = "1.2.13")
bazel_dep(name = "rules_cc", version = "0.0.8")
bazel_dep(name = "rules_java", version = "6.2.2")
diff --git a/distdir_deps.bzl b/distdir_deps.bzl
index cf9bd19..ddfa94b 100644
--- a/distdir_deps.bzl
+++ b/distdir_deps.bzl
@@ -272,7 +272,7 @@
"package_version": "1.5.2-3",
},
"blake3": {
- "archive": "v1.3.3.zip",
+ "archive": "1.3.3.zip",
"sha256": "bb529ba133c0256df49139bd403c17835edbf60d2ecd6463549c6a5fe279364d",
"urls": [
"https://github.com/BLAKE3-team/BLAKE3/archive/refs/tags/1.3.3.zip",
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 27ac03a..ac88542 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -96,6 +96,7 @@
"//src/main/java/com/google/devtools/build/lib/util:srcs",
"//src/main/java/com/google/devtools/build/lib/versioning:srcs",
"//src/main/java/com/google/devtools/build/lib/vfs:srcs",
+ "//src/main/java/com/google/devtools/build/lib/vfs/bazel:srcs",
"//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs:srcs",
"//src/main/java/com/google/devtools/build/lib/windows:srcs",
"//src/main/java/com/google/devtools/build/lib/worker:srcs",
@@ -443,6 +444,7 @@
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs:output_service",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
+ "//src/main/java/com/google/devtools/build/lib/vfs/bazel",
"//src/main/java/com/google/devtools/build/lib/windows",
"//src/main/java/com/google/devtools/build/lib/worker:worker_metric",
"//src/main/java/com/google/devtools/build/skyframe",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/BUILD
index 087438d..4097239 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BUILD
@@ -157,6 +157,7 @@
"//src/main/java/com/google/devtools/build/lib/util:os",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
+ "//src/main/java/com/google/devtools/build/lib/vfs/bazel",
"//src/main/java/com/google/devtools/build/lib/windows",
"//src/main/java/com/google/devtools/common/options",
"//src/main/protobuf:failure_details_java_proto",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelFileSystemModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelFileSystemModule.java
index 1027f3a..f5af64e 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelFileSystemModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelFileSystemModule.java
@@ -31,6 +31,7 @@
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.bazel.BazelHashFunctions;
import com.google.devtools.build.lib.windows.WindowsFileSystem;
import com.google.devtools.common.options.OptionsParsingException;
import com.google.devtools.common.options.OptionsParsingResult;
@@ -44,6 +45,10 @@
* com.google.devtools.build.lib.vfs.FileSystem} class use {@code SHA256} by default.
*/
public class BazelFileSystemModule extends BlazeModule {
+ static {
+ BazelHashFunctions.ensureRegistered();
+ }
+
@Override
public ModuleFileSystem getFileSystem(
OptionsParsingResult startupOptions, PathFragment realExecRootBase)
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/bazel/BUILD b/src/main/java/com/google/devtools/build/lib/vfs/bazel/BUILD
new file mode 100644
index 0000000..fde90bc
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/bazel/BUILD
@@ -0,0 +1,28 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+ default_applicable_licenses = ["//:license"],
+ default_visibility = ["//src:__subpackages__"],
+)
+
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//src:__subpackages__"],
+)
+
+java_library(
+ name = "bazel",
+ srcs = glob(
+ [
+ "*.java",
+ ],
+ ),
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib/jni",
+ "//src/main/java/com/google/devtools/build/lib/vfs",
+ "//third_party:error_prone_annotations",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ ],
+)
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/bazel/BazelHashFunctions.java b/src/main/java/com/google/devtools/build/lib/vfs/bazel/BazelHashFunctions.java
new file mode 100644
index 0000000..c917441
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/bazel/BazelHashFunctions.java
@@ -0,0 +1,45 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.vfs.bazel;
+
+import com.google.devtools.build.lib.jni.JniLoader;
+import com.google.devtools.build.lib.vfs.DigestHashFunction;
+import java.security.Security;
+import javax.annotation.Nullable;
+
+/** Bazel specific {@link DigestHashFunction}s. */
+public final class BazelHashFunctions {
+ @Nullable public static final DigestHashFunction BLAKE3;
+
+ static {
+ DigestHashFunction hashFunction = null;
+
+ if (JniLoader.isJniAvailable()) {
+ try {
+ Security.addProvider(new Blake3Provider());
+ hashFunction = DigestHashFunction.register(new Blake3HashFunction(), "BLAKE3");
+ } catch (UnsatisfiedLinkError ignored) {
+ // This can happen if bazel was compiled manually (with compile.sh),
+ // on windows. In that case jni is available, but missing the blake3
+ // symbols necessary to register the hasher.
+ }
+ }
+
+ BLAKE3 = hashFunction;
+ }
+
+ public static void ensureRegistered() {}
+
+ private BazelHashFunctions() {}
+}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3HashFunction.java b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3HashFunction.java
new file mode 100644
index 0000000..0b203ba
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3HashFunction.java
@@ -0,0 +1,88 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.vfs.bazel;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkPositionIndexes;
+
+import com.google.common.hash.Funnel;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hasher;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/** A {@link HashFunction} for BLAKE3. */
+public final class Blake3HashFunction implements HashFunction {
+ @Override
+ public int bits() {
+ return 256;
+ }
+
+ @Override
+ public Hasher newHasher() {
+ return new Blake3Hasher(new Blake3MessageDigest());
+ }
+
+ @Override
+ public Hasher newHasher(int expectedInputSize) {
+ checkArgument(
+ expectedInputSize >= 0, "expectedInputSize must be >= 0 but was %s", expectedInputSize);
+ return newHasher();
+ }
+
+ /* The following methods implement the {HashFunction} interface. */
+
+ @Override
+ public <T> HashCode hashObject(T instance, Funnel<? super T> funnel) {
+ return newHasher().putObject(instance, funnel).hash();
+ }
+
+ @Override
+ public HashCode hashUnencodedChars(CharSequence input) {
+ int len = input.length();
+ return newHasher(len * 2).putUnencodedChars(input).hash();
+ }
+
+ @Override
+ public HashCode hashString(CharSequence input, Charset charset) {
+ return newHasher().putString(input, charset).hash();
+ }
+
+ @Override
+ public HashCode hashInt(int input) {
+ return newHasher(4).putInt(input).hash();
+ }
+
+ @Override
+ public HashCode hashLong(long input) {
+ return newHasher(8).putLong(input).hash();
+ }
+
+ @Override
+ public HashCode hashBytes(byte[] input) {
+ return hashBytes(input, 0, input.length);
+ }
+
+ @Override
+ public HashCode hashBytes(byte[] input, int off, int len) {
+ checkPositionIndexes(off, off + len, input.length);
+ return newHasher(len).putBytes(input, off, len).hash();
+ }
+
+ @Override
+ public HashCode hashBytes(ByteBuffer input) {
+ return newHasher(input.remaining()).putBytes(input).hash();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3Hasher.java b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3Hasher.java
new file mode 100644
index 0000000..180d62b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3Hasher.java
@@ -0,0 +1,146 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.vfs.bazel;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.hash.Funnel;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hasher;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/** A {@link Hasher} for BLAKE3. */
+public final class Blake3Hasher implements Hasher {
+ private final Blake3MessageDigest messageDigest;
+ private boolean isDone = false;
+
+ public Blake3Hasher(Blake3MessageDigest blake3MessageDigest) {
+ messageDigest = blake3MessageDigest;
+ }
+
+ /* The following methods implement the {Hasher} interface. */
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putBytes(ByteBuffer b) {
+ messageDigest.engineUpdate(b);
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putBytes(byte[] bytes, int off, int len) {
+ messageDigest.engineUpdate(bytes, off, len);
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putBytes(byte[] bytes) {
+ messageDigest.engineUpdate(bytes, 0, bytes.length);
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putByte(byte b) {
+ messageDigest.engineUpdate(b);
+ return this;
+ }
+
+ @Override
+ public HashCode hash() {
+ checkState(!isDone);
+ isDone = true;
+
+ return HashCode.fromBytes(messageDigest.engineDigest());
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public final Hasher putBoolean(boolean b) {
+ return putByte(b ? (byte) 1 : (byte) 0);
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public final Hasher putDouble(double d) {
+ return putLong(Double.doubleToRawLongBits(d));
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public final Hasher putFloat(float f) {
+ return putInt(Float.floatToRawIntBits(f));
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putUnencodedChars(CharSequence charSequence) {
+ for (int i = 0, len = charSequence.length(); i < len; i++) {
+ putChar(charSequence.charAt(i));
+ }
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putString(CharSequence charSequence, Charset charset) {
+ return putBytes(charSequence.toString().getBytes(charset));
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putShort(short s) {
+ putByte((byte) s);
+ putByte((byte) (s >>> 8));
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putInt(int i) {
+ putByte((byte) i);
+ putByte((byte) (i >>> 8));
+ putByte((byte) (i >>> 16));
+ putByte((byte) (i >>> 24));
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putLong(long l) {
+ for (int i = 0; i < 64; i += 8) {
+ putByte((byte) (l >>> i));
+ }
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public Hasher putChar(char c) {
+ putByte((byte) c);
+ putByte((byte) (c >>> 8));
+ return this;
+ }
+
+ @Override
+ @CanIgnoreReturnValue
+ public <T> Hasher putObject(T instance, Funnel<? super T> funnel) {
+ funnel.funnel(instance, this);
+ return this;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3MessageDigest.java b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3MessageDigest.java
new file mode 100644
index 0000000..50d2ace
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3MessageDigest.java
@@ -0,0 +1,139 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.vfs.bazel;
+
+import static java.lang.Math.min;
+
+import com.google.devtools.build.lib.jni.JniLoader;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+
+/** A {@link MessageDigest} for BLAKE3. */
+public final class Blake3MessageDigest extends MessageDigest {
+ // These constants match the native definitions in:
+ // https://github.com/BLAKE3-team/BLAKE3/blob/master/c/blake3.h
+ public static final int KEY_LEN = 32;
+ public static final int OUT_LEN = 32;
+
+ static {
+ JniLoader.loadJni();
+ }
+
+ private static final int STATE_SIZE = hasher_size();
+ private static final byte[] INITIAL_STATE = new byte[STATE_SIZE];
+
+ static {
+ initialize_hasher(INITIAL_STATE);
+ }
+
+ // To reduce the number of calls made via JNI, buffer up to this many bytes
+ // before updating the hasher.
+ public static final int ONESHOT_THRESHOLD = 8 * 1024;
+
+ private final ByteBuffer buffer = ByteBuffer.allocate(ONESHOT_THRESHOLD);
+ private final byte[] hasher = new byte[STATE_SIZE];
+
+ public Blake3MessageDigest() {
+ super("BLAKE3");
+ System.arraycopy(INITIAL_STATE, 0, hasher, 0, STATE_SIZE);
+ }
+
+ private void flush() {
+ if (buffer.position() > 0) {
+ blake3_hasher_update(hasher, buffer.array(), buffer.position());
+ buffer.clear();
+ }
+ }
+
+ @Override
+ public void engineUpdate(byte[] data, int offset, int length) {
+ while (length > 0) {
+ int numToCopy = min(length, buffer.remaining());
+ buffer.put(data, offset, numToCopy);
+ length -= numToCopy;
+ offset += numToCopy;
+
+ if (buffer.remaining() == 0) {
+ flush();
+ }
+ }
+ }
+
+ @Override
+ public void engineUpdate(byte b) {
+ if (buffer.remaining() == 0) {
+ flush();
+ }
+ buffer.put(b);
+ }
+
+ @Override
+ public void engineUpdate(ByteBuffer input) {
+ super.engineUpdate(input);
+ }
+
+ private byte[] getOutput(int outputLength) {
+ flush();
+
+ byte[] retByteArray = new byte[outputLength];
+ blake3_hasher_finalize(hasher, retByteArray, outputLength);
+
+ engineReset();
+ return retByteArray;
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+
+ @Override
+ public void engineReset() {
+ buffer.clear();
+ System.arraycopy(INITIAL_STATE, 0, hasher, 0, STATE_SIZE);
+ }
+
+ @Override
+ public int engineGetDigestLength() {
+ return OUT_LEN;
+ }
+
+ @Override
+ public byte[] engineDigest() {
+ return getOutput(OUT_LEN);
+ }
+
+ @Override
+ public int engineDigest(byte[] buf, int off, int len) throws DigestException {
+ if (len < OUT_LEN) {
+ throw new DigestException("partial digests not returned");
+ }
+ if (buf.length - off < OUT_LEN) {
+ throw new DigestException("insufficient space in the output buffer to store the digest");
+ }
+
+ byte[] digestBytes = getOutput(OUT_LEN);
+ System.arraycopy(digestBytes, 0, buf, off, digestBytes.length);
+ return digestBytes.length;
+ }
+
+ public static final native int hasher_size();
+
+ public static final native void initialize_hasher(byte[] hasher);
+
+ public static final native void blake3_hasher_update(byte[] hasher, byte[] input, int inputLen);
+
+ public static final native void blake3_hasher_finalize(byte[] hasher, byte[] out, int outLen);
+}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3Provider.java b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3Provider.java
new file mode 100644
index 0000000..0c86814
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/vfs/bazel/Blake3Provider.java
@@ -0,0 +1,24 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.vfs.bazel;
+
+import java.security.Provider;
+
+/** A {@link Provider} for BLAKE3. */
+public final class Blake3Provider extends Provider {
+ public Blake3Provider() {
+ super("BLAKE3Provider", "1.0", "A BLAKE3 digest provider");
+ put("MessageDigest.BLAKE3", Blake3MessageDigest.class.getName());
+ }
+}
diff --git a/src/main/native/BUILD b/src/main/native/BUILD
index 2601d32..c011aef 100644
--- a/src/main/native/BUILD
+++ b/src/main/native/BUILD
@@ -54,6 +54,21 @@
includes = ["."], # For jni headers.
)
+cc_library(
+ name = "blake3_jni",
+ srcs = [
+ "blake3_jni.cc",
+ ":jni.h",
+ ":jni_md.h",
+ ],
+ includes = ["."], # For jni headers.
+ visibility = ["//src/main/native:__subpackages__"],
+ deps = [
+ "@blake3",
+ ],
+ alwayslink = 1,
+)
+
cc_binary(
name = "libunix_jni.so",
srcs = [
@@ -80,6 +95,7 @@
linkshared = 1,
visibility = ["//src/main/java/com/google/devtools/build/lib/jni:__pkg__"],
deps = [
+ ":blake3_jni",
":latin1_jni_path",
"//src/main/cpp/util:logging",
"//src/main/cpp/util:md5",
diff --git a/src/main/native/blake3_jni.cc b/src/main/native/blake3_jni.cc
new file mode 100644
index 0000000..5620188
--- /dev/null
+++ b/src/main/native/blake3_jni.cc
@@ -0,0 +1,73 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "c/blake3.h"
+
+namespace blaze_jni {
+
+jbyte *get_byte_array(JNIEnv *env, jbyteArray java_array) {
+ return (jbyte *)env->GetPrimitiveArrayCritical(java_array, nullptr);
+}
+
+void release_byte_array(JNIEnv *env, jbyteArray array, jbyte *addr) {
+ env->ReleasePrimitiveArrayCritical(array, addr, 0);
+}
+
+extern "C" JNIEXPORT int JNICALL
+Java_com_google_devtools_build_lib_vfs_bazel_Blake3MessageDigest_hasher_1size(
+ JNIEnv *env, jobject obj) {
+ return (int)sizeof(blake3_hasher);
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_devtools_build_lib_vfs_bazel_Blake3MessageDigest_initialize_1hasher(
+ JNIEnv *env, jobject obj, jbyteArray jhasher) {
+ blake3_hasher *hasher = (blake3_hasher *)get_byte_array(env, jhasher);
+ if (hasher) {
+ blake3_hasher_init(hasher);
+ release_byte_array(env, jhasher, (jbyte *)hasher);
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_devtools_build_lib_vfs_bazel_Blake3MessageDigest_blake3_1hasher_1update(
+ JNIEnv *env, jobject obj, jbyteArray jhasher, jbyteArray input,
+ jint input_len) {
+ blake3_hasher *hasher = (blake3_hasher *)get_byte_array(env, jhasher);
+ if (hasher) {
+ jbyte *input_addr = get_byte_array(env, input);
+ blake3_hasher_update(hasher, input_addr, input_len);
+ release_byte_array(env, input, input_addr);
+ release_byte_array(env, jhasher, (jbyte *)hasher);
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_devtools_build_lib_vfs_bazel_Blake3MessageDigest_blake3_1hasher_1finalize(
+ JNIEnv *env, jobject obj, jbyteArray jhasher, jbyteArray out,
+ jint out_len) {
+ blake3_hasher *hasher = (blake3_hasher *)get_byte_array(env, jhasher);
+ if (hasher) {
+ jbyte *out_addr = get_byte_array(env, out);
+ blake3_hasher_finalize(hasher, (uint8_t *)out_addr, out_len);
+ release_byte_array(env, out, out_addr);
+ release_byte_array(env, jhasher, (jbyte *)hasher);
+ }
+}
+
+} // namespace blaze_jni
diff --git a/src/main/native/windows/BUILD b/src/main/native/windows/BUILD
index 73eaeed..b595e75 100644
--- a/src/main/native/windows/BUILD
+++ b/src/main/native/windows/BUILD
@@ -76,6 +76,7 @@
deps = [
":lib-file",
":lib-process",
+ "//src/main/native:blake3_jni",
],
)
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/BUILD b/src/test/java/com/google/devtools/build/lib/vfs/BUILD
index 8b9e008..d4856a8 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/vfs/BUILD
@@ -10,6 +10,7 @@
name = "srcs",
testonly = 0,
srcs = glob(["**"]) + [
+ "//src/test/java/com/google/devtools/build/lib/vfs/bazel:srcs",
"//src/test/java/com/google/devtools/build/lib/vfs/util:srcs",
],
visibility = ["//src:__subpackages__"],
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/bazel/BUILD b/src/test/java/com/google/devtools/build/lib/vfs/bazel/BUILD
new file mode 100644
index 0000000..f1d34ce
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/vfs/bazel/BUILD
@@ -0,0 +1,42 @@
+load("@rules_java//java:defs.bzl", "java_library", "java_test")
+
+package(
+ default_applicable_licenses = ["//:license"],
+ default_testonly = 1,
+ default_visibility = ["//src:__subpackages__"],
+)
+
+filegroup(
+ name = "srcs",
+ testonly = 0,
+ srcs = glob(["**"]),
+ visibility = ["//src:__subpackages__"],
+)
+
+java_library(
+ name = "BazelTests_lib",
+ srcs = glob(
+ [
+ "*.java",
+ ],
+ ),
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib/vfs/bazel",
+ "//src/test/java/com/google/devtools/build/lib/testutil",
+ "//third_party:guava",
+ "//third_party:guava-testlib",
+ "//third_party:junit4",
+ "//third_party:truth",
+ "//third_party/protobuf:protobuf_java",
+ ],
+)
+
+java_test(
+ name = "BazelTests",
+ size = "small",
+ test_class = "com.google.devtools.build.lib.AllTests",
+ runtime_deps = [
+ ":BazelTests_lib",
+ "//src/test/java/com/google/devtools/build/lib:test_runner",
+ ],
+)
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/bazel/Blake3HasherTest.java b/src/test/java/com/google/devtools/build/lib/vfs/bazel/Blake3HasherTest.java
new file mode 100644
index 0000000..9fa0cb3
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/vfs/bazel/Blake3HasherTest.java
@@ -0,0 +1,48 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.vfs.bazel;
+
+import static org.junit.Assert.assertEquals;
+
+import java.nio.charset.StandardCharsets;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link Blake3MessageDigest}. */
+@RunWith(JUnit4.class)
+public class Blake3HasherTest {
+ @Test
+ public void emptyHash() {
+ Blake3Hasher h = new Blake3Hasher(new Blake3MessageDigest());
+
+ byte[] data = new byte[0];
+ h.putBytes(data);
+
+ assertEquals(
+ "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262", h.hash().toString());
+ }
+
+ @Test
+ public void helloWorld() {
+ Blake3Hasher h = new Blake3Hasher(new Blake3MessageDigest());
+
+ byte[] data = "hello world".getBytes(StandardCharsets.US_ASCII);
+ h.putBytes(data);
+
+ assertEquals(
+ "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24", h.hash().toString());
+ }
+}
diff --git a/third_party/blake3/blake3.BUILD b/third_party/blake3/blake3.BUILD
index 6e96f05..867e33f 100644
--- a/third_party/blake3/blake3.BUILD
+++ b/third_party/blake3/blake3.BUILD
@@ -30,7 +30,9 @@
] + select({
"@bazel_tools//src/conditions:linux_x86_64": [
"c/blake3_avx2_x86-64_unix.S",
- "c/blake3_avx512_x86-64_unix.S",
+ # Disable to appease bazel-ci which uses ubuntu-18 (EOL) and GCC 7
+ # lacking the headers to compile AVX512.
+ # "c/blake3_avx512_x86-64_unix.S",
"c/blake3_sse2_x86-64_unix.S",
"c/blake3_sse41_x86-64_unix.S",
],
@@ -50,16 +52,21 @@
"c/blake3_impl.h",
],
copts = select({
- "@bazel_tools//src/conditions:linux_x86_64": [],
+ "@bazel_tools//src/conditions:linux_x86_64": [
+ # Disable to appease bazel-ci which uses ubuntu-18 (EOL) and GCC 7
+ # lacking the headers to compile AVX512.
+ "-DBLAKE3_NO_AVX512",
+ ],
"@bazel_tools//src/conditions:windows_x64": [],
"@bazel_tools//src/conditions:darwin_arm64": [
"-DBLAKE3_USE_NEON=1",
],
"//conditions:default": [
- "-DBLAKE3_NO_SSE2",
- "-DBLAKE3_NO_SSE41",
"-DBLAKE3_NO_AVX2",
"-DBLAKE3_NO_AVX512",
+ "-DBLAKE3_NO_NEON",
+ "-DBLAKE3_NO_SSE2",
+ "-DBLAKE3_NO_SSE41",
],
}),
includes = ["."],