org.gradle.internal.hash.Hashing Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 5.5.1 API redistribution.
/*
* Copyright 2017 the original author or authors.
*
* 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 org.gradle.internal.hash;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import org.gradle.internal.io.BufferCaster;
import org.gradle.internal.io.NullOutputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Some popular hash functions. Replacement for Guava's hashing utilities.
* Inspired by the Google Guava project – https://github.com/google/guava.
*/
public class Hashing {
private Hashing() {}
private static final HashFunction MD5 = MessageDigestHashFunction.of("MD5");
private static final HashFunction SHA1 = MessageDigestHashFunction.of("SHA-1");
private static final HashFunction DEFAULT = MD5;
/**
* Returns a new {@link Hasher} based on the default hashing implementation.
*/
public static Hasher newHasher() {
return DEFAULT.newHasher();
}
/**
* Returns a new {@link PrimitiveHasher} based on the default hashing implementation.
*/
public static PrimitiveHasher newPrimitiveHasher() {
return DEFAULT.newPrimitiveHasher();
}
/**
* Returns a hash code to use as a signature for a given type.
*/
public static HashCode signature(Class type) {
return signature("CLASS:" + type.getName());
}
/**
* Returns a hash code to use as a signature for a given thing.
*/
public static HashCode signature(String thing) {
Hasher hasher = DEFAULT.newHasher();
hasher.putString("SIGNATURE");
hasher.putString(thing);
return hasher.hash();
}
/**
* Hash the given bytes with the default hash function.
*/
public static HashCode hashBytes(byte[] bytes) {
return DEFAULT.hashBytes(bytes);
}
/**
* Hash the given string with the default hash function.
*/
public static HashCode hashString(CharSequence string) {
return DEFAULT.hashString(string);
}
/**
* Creates a {@link HashingOutputStream} with the default hash function.
*/
public static HashingOutputStream primitiveStreamHasher() {
return primitiveStreamHasher(NullOutputStream.INSTANCE);
}
/**
* Creates a {@link HashingOutputStream} with the default hash function.
*/
public static HashingOutputStream primitiveStreamHasher(OutputStream output) {
return new HashingOutputStream(DEFAULT, output);
}
/**
* The default hashing function.
*/
public static HashFunction defaultFunction() {
return DEFAULT;
}
/**
* MD5 hashing function.
*/
public static HashFunction md5() {
return MD5;
}
/**
* SHA1 hashing function.
*/
public static HashFunction sha1() {
return SHA1;
}
private static abstract class MessageDigestHashFunction implements HashFunction {
public static MessageDigestHashFunction of(String algorithm) {
MessageDigest prototype;
try {
prototype = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Cannot instantiate digest algorithm: " + algorithm);
}
try {
prototype.clone();
return new CloningMessageDigestHashFunction(prototype);
} catch (CloneNotSupportedException e) {
return new RegularMessageDigestHashFunction(algorithm);
}
}
@Override
public PrimitiveHasher newPrimitiveHasher() {
MessageDigest digest = createDigest();
return new MessageDigestHasher(digest);
}
@Override
public Hasher newHasher() {
return new DefaultHasher(newPrimitiveHasher());
}
@Override
public HashCode hashBytes(byte[] bytes) {
PrimitiveHasher hasher = newPrimitiveHasher();
hasher.putBytes(bytes);
return hasher.hash();
}
@Override
public HashCode hashString(CharSequence string) {
PrimitiveHasher hasher = newPrimitiveHasher();
hasher.putString(string);
return hasher.hash();
}
protected abstract MessageDigest createDigest();
}
private static class CloningMessageDigestHashFunction extends MessageDigestHashFunction {
private final MessageDigest prototype;
public CloningMessageDigestHashFunction(MessageDigest prototype) {
this.prototype = prototype;
}
@Override
protected MessageDigest createDigest() {
try {
return (MessageDigest) prototype.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
}
private static class RegularMessageDigestHashFunction extends MessageDigestHashFunction {
private final String algorithm;
public RegularMessageDigestHashFunction(String algorithm) {
this.algorithm = algorithm;
}
@Override
protected MessageDigest createDigest() {
try {
return MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
}
private static class MessageDigestHasher implements PrimitiveHasher {
private final ByteBuffer buffer = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
private MessageDigest digest;
public MessageDigestHasher(MessageDigest digest) {
this.digest = digest;
}
private MessageDigest getDigest() {
if (digest == null) {
throw new IllegalStateException("Cannot reuse hasher!");
}
return digest;
}
@Override
public void putByte(byte b) {
getDigest().update(b);
}
@Override
public void putBytes(byte[] bytes) {
getDigest().update(bytes);
}
@Override
public void putBytes(byte[] bytes, int off, int len) {
getDigest().update(bytes, off, len);
}
private void update(int length) {
getDigest().update(buffer.array(), 0, length);
BufferCaster.cast(buffer).clear();
}
@Override
public void putInt(int value) {
buffer.putInt(value);
update(4);
}
@Override
public void putLong(long value) {
buffer.putLong(value);
update(8);
}
@Override
public void putDouble(double value) {
long longValue = Double.doubleToRawLongBits(value);
putLong(longValue);
}
@Override
public void putBoolean(boolean value) {
putByte((byte) (value ? 1 : 0));
}
@Override
public void putString(CharSequence value) {
putBytes(value.toString().getBytes(Charsets.UTF_8));
}
@Override
public void putHash(HashCode hashCode) {
putBytes(hashCode.getBytes());
}
@Override
public HashCode hash() {
byte[] bytes = getDigest().digest();
digest = null;
return HashCode.fromBytesNoCopy(bytes);
}
}
private static class DefaultHasher implements Hasher {
private final PrimitiveHasher hasher;
private String invalidReason;
public DefaultHasher(PrimitiveHasher unsafeHasher) {
this.hasher = unsafeHasher;
}
@Override
public void putByte(byte value) {
hasher.putInt(1);
hasher.putByte(value);
}
@Override
public void putBytes(byte[] bytes) {
hasher.putInt(bytes.length);
hasher.putBytes(bytes);
}
@Override
public void putBytes(byte[] bytes, int off, int len) {
hasher.putInt(len);
hasher.putBytes(bytes, off, len);
}
@Override
public void putHash(HashCode hashCode) {
hasher.putInt(hashCode.length());
hasher.putHash(hashCode);
}
@Override
public void putInt(int value) {
hasher.putInt(4);
hasher.putInt(value);
}
@Override
public void putLong(long value) {
hasher.putInt(8);
hasher.putLong(value);
}
@Override
public void putDouble(double value) {
hasher.putInt(8);
hasher.putDouble(value);
}
@Override
public void putBoolean(boolean value) {
hasher.putInt(1);
hasher.putBoolean(value);
}
@Override
public void putString(CharSequence value) {
hasher.putInt(value.length());
hasher.putString(value);
}
@Override
public void putNull() {
this.putInt(0);
}
@Override
public void markAsInvalid(String invalidReason) {
this.invalidReason = invalidReason;
}
@Override
public boolean isValid() {
return invalidReason == null;
}
@Override
public String getInvalidReason() {
return Preconditions.checkNotNull(invalidReason);
}
@Override
public HashCode hash() {
if (!isValid()) {
throw new IllegalStateException("Hash is invalid: " + getInvalidReason());
}
return hasher.hash();
}
}
}