1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.cache; 18 19 import build.bazel.remote.execution.v2.Digest; 20 import build.bazel.remote.execution.v2.DigestFunction; 21 import com.google.common.base.Preconditions; 22 import com.google.common.hash.HashCode; 23 import com.google.common.hash.HashFunction; 24 import com.google.common.hash.Hashing; 25 import com.google.common.io.ByteSource; 26 import com.google.protobuf.Message; 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.nio.file.Files; 32 33 /** Utility methods to compute {@link Digest}. */ 34 public class DigestCalculator { 35 public static final DigestFunction.Value DIGEST_FUNCTION = DigestFunction.Value.SHA256; 36 private static final HashFunction HASH_FUNCTION = Hashing.sha256(); 37 38 /** 39 * Computes a digest for a file. 40 * 41 * @param file the {@link File} that the digest is calculated for 42 * @return the {@link Digest} of the {@code file} 43 */ compute(File file)44 public static Digest compute(File file) throws IOException { 45 if (!file.exists() || !file.isFile()) { 46 throw new IllegalArgumentException("File does not exist or is not a file!"); 47 } 48 49 byte[] digest = 50 new ByteSource() { 51 @Override 52 public InputStream openStream() throws IOException { 53 return new FileInputStream(file); 54 } 55 }.hash(HASH_FUNCTION).asBytes(); 56 Preconditions.checkNotNull(digest, "Missing digest for %s", file.getAbsolutePath()); 57 return DigestCalculator.buildDigest(digest, Files.size(file.toPath())); 58 } 59 60 /** 61 * Computes a digest for a proto message. 62 * 63 * @param message the {@link Message} that the digest is calculated for 64 * @return the {@link Digest} of the {@code message} 65 */ compute(Message message)66 public static Digest compute(Message message) { 67 return DigestCalculator.compute(message.toByteArray()); 68 } 69 70 /** 71 * Computes a digest for a byte array. 72 * 73 * @param blob the byte array that the digest is calculated for 74 * @return the {@link Digest} of the {@code blob} 75 */ compute(byte[] blob)76 public static Digest compute(byte[] blob) { 77 return DigestCalculator.buildDigest(HASH_FUNCTION.hashBytes(blob).asBytes(), blob.length); 78 } 79 buildDigest(byte[] hash, long size)80 private static Digest buildDigest(byte[] hash, long size) { 81 return Digest.newBuilder() 82 .setHash(HashCode.fromBytes(hash).toString()) 83 .setSizeBytes(size) 84 .build(); 85 } 86 } 87