blob: 49e9ce515d725ea5a3b146a143a7d262f98447f7 [file] [log] [blame]
// Copyright 2020 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.actions.cache;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.VarInt;
import com.google.devtools.build.lib.vfs.DigestUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Map;
import javax.annotation.Nullable;
/** Utility class for digests/metadata relating to the action cache. */
public final class MetadataDigestUtils {
private MetadataDigestUtils() {}
/**
* @param source the byte buffer source.
* @return the digest from the given buffer.
*/
public static byte[] read(ByteBuffer source) {
int size = VarInt.getVarInt(source);
byte[] bytes = new byte[size];
source.get(bytes);
return bytes;
}
/** Write the digest to the output stream. */
public static void write(byte[] digest, OutputStream sink) throws IOException {
VarInt.putVarInt(digest.length, sink);
sink.write(digest);
}
/**
* Computes an order-independent digest from the given (path, metadata) pairs.
*
* <p>Note that as discussed in https://github.com/bazelbuild/bazel/issues/15660, using {@link
* DigestUtils#xor} to achieve order-independence is questionable in case it is possible that
* multiple string keys map to the same bytes when passed through {@link Fingerprint#addString}
* (due to lossy conversion from UTF-16 to UTF-8). We expect however that paths are represented as
* latin1 bytes encoded as a string, so the concern does not apply.
*
* @param mdMap A collection of (execPath, FileArtifactValue) pairs. Values may be null.
*/
public static byte[] fromMetadata(Map<String, FileArtifactValue> mdMap) {
byte[] result = new byte[1]; // reserve the empty string
// Profiling showed that MessageDigest engine instantiation was a hotspot, so create one
// instance for this computation to amortize its cost.
Fingerprint fp = new Fingerprint();
for (Map.Entry<String, FileArtifactValue> entry : mdMap.entrySet()) {
result = DigestUtils.xor(result, getDigest(fp, entry.getKey(), entry.getValue()));
}
return result;
}
private static byte[] getDigest(Fingerprint fp, String execPath, @Nullable FileArtifactValue md) {
fp.addString(execPath);
if (md != null) {
md.addTo(fp);
}
return fp.digestAndReset();
}
}