Add ActionSketch as a basis for top-down action caching.
RELNOTES: None
PiperOrigin-RevId: 259451621
diff --git a/src/main/java/com/google/devtools/build/lib/actionsketch/ActionSketch.java b/src/main/java/com/google/devtools/build/lib/actionsketch/ActionSketch.java
new file mode 100644
index 0000000..16ddbe8
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actionsketch/ActionSketch.java
@@ -0,0 +1,102 @@
+// Copyright 2019 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.actionsketch;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Preconditions;
+import com.google.protobuf.ByteString;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import javax.annotation.Nullable;
+
+/**
+ * An {@link ActionSketch} encodes a transitive hash of an action sufficient to associate with it
+ * the result of executing the action. Therefore, this must include a hash on some upper bound of
+ * all transitively consumed input files, as well as a transitive hash of all action keys.
+ */
+@AutoValue
+public abstract class ActionSketch {
+ public static final int BIGINTEGER_ENCODED_LENGTH = /*length=*/ 1 + /*payload=*/ 17;
+ public static final int MAX_BYTES = /*hashes=*/ 2 * BIGINTEGER_ENCODED_LENGTH;
+
+ @Nullable
+ public abstract BigInteger transitiveSourceHash();
+
+ @Nullable
+ public abstract BigInteger transitiveActionLookupHash();
+
+ public static Builder builder() {
+ return new AutoValue_ActionSketch.Builder();
+ }
+
+ public abstract Builder toBuilder();
+
+ /** A builder for {@link ActionSketch}. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder setTransitiveSourceHash(BigInteger transitiveSourceHash);
+
+ public abstract Builder setTransitiveActionLookupHash(BigInteger transitiveActionLookupHash);
+
+ public abstract ActionSketch build();
+ }
+
+ public ByteString toBytes() {
+ ByteBuffer buffer = ByteBuffer.allocate(MAX_BYTES);
+ writeTo(buffer);
+ return ByteString.copyFrom(buffer.array(), 0, buffer.position());
+ }
+
+ public void writeTo(ByteBuffer buffer) {
+ writeNextValue(transitiveSourceHash(), buffer);
+ writeNextValue(transitiveActionLookupHash(), buffer);
+ }
+
+ public static void writeNextValue(@Nullable BigInteger value, ByteBuffer buffer) {
+ if (value == null) {
+ buffer.put((byte) -1);
+ } else {
+ byte[] bytes = value.toByteArray();
+ Preconditions.checkState(
+ bytes.length > 0 && bytes.length <= 17,
+ "Illegal number of bytes in sketch field? %s",
+ bytes.length);
+ buffer.put((byte) bytes.length).put(bytes);
+ }
+ }
+
+ public static ActionSketch fromBytes(ByteString inputBytes) {
+ return fromByteBuffer(inputBytes.asReadOnlyByteBuffer());
+ }
+
+ public static ActionSketch fromByteBuffer(ByteBuffer buffer) {
+ Builder builder =
+ builder()
+ .setTransitiveSourceHash(readNextValue(buffer))
+ .setTransitiveActionLookupHash(readNextValue(buffer));
+ return builder.build();
+ }
+
+ @Nullable
+ public static BigInteger readNextValue(ByteBuffer buffer) {
+ byte length = buffer.get();
+ if (length < 0) {
+ return null;
+ }
+ byte[] val = new byte[length];
+ buffer.get(val);
+ return new BigInteger(val);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/actionsketch/Sketches.java b/src/main/java/com/google/devtools/build/lib/actionsketch/Sketches.java
new file mode 100644
index 0000000..1f82283
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actionsketch/Sketches.java
@@ -0,0 +1,49 @@
+// Copyright 2019 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.actionsketch;
+
+import com.google.common.hash.HashCode;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.ActionKeyContext;
+import com.google.devtools.build.lib.actions.Artifact;
+import java.math.BigInteger;
+
+/** Utilities for dealing with {@link ActionSketch} sketches. */
+public class Sketches {
+ public static BigInteger fromHashCode(HashCode hashCode) {
+ return new BigInteger(/*signum=*/ 1, hashCode.asBytes());
+ }
+
+ /**
+ * Compute the hash of the direct action key for the given action, including the names of its
+ * output files.
+ */
+ public static BigInteger computeActionKey(
+ ActionAnalysisMetadata action, ActionKeyContext keyContext) {
+ Hasher hasher = newHasher().putUnencodedChars(action.getKey(keyContext));
+ for (Artifact output : action.getOutputs()) {
+ hasher.putUnencodedChars(output.getExecPath().getPathString());
+ }
+ return fromHashCode(hasher.hash());
+ }
+
+ public static Hasher newHasher() {
+ return Hashing.murmur3_128().newHasher();
+ }
+
+ private Sketches() {}
+}
diff --git a/src/test/java/com/google/devtools/build/lib/actionsketch/ActionSketchTest.java b/src/test/java/com/google/devtools/build/lib/actionsketch/ActionSketchTest.java
new file mode 100644
index 0000000..60abccb
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/actionsketch/ActionSketchTest.java
@@ -0,0 +1,40 @@
+// Copyright 2019 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.actionsketch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.math.BigInteger;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link ActionSketch}. */
+@RunWith(JUnit4.class)
+public final class ActionSketchTest {
+
+ @Test
+ public void serialization() {
+ roundTrip(
+ ActionSketch.builder()
+ .setTransitiveSourceHash(BigInteger.ONE)
+ .setTransitiveActionLookupHash(new BigInteger("123456789"))
+ .build());
+ }
+
+ private static void roundTrip(ActionSketch sketch) {
+ assertThat(ActionSketch.fromBytes(sketch.toBytes())).isEqualTo(sketch);
+ }
+}