Port hashing stuff to Kotlin

This commit is contained in:
comp500 2019-12-19 14:58:07 +00:00
parent b45a2983e7
commit ecaab219c2
12 changed files with 194 additions and 295 deletions

View File

@ -1,18 +0,0 @@
package link.infra.packwiz.installer.metadata.hash;
import okio.ForwardingSource;
import okio.Source;
public abstract class GeneralHashingSource extends ForwardingSource {
public GeneralHashingSource(Source delegate) {
super(delegate);
}
public abstract Hash getHash();
public boolean hashIsEqual(Object compareTo) {
return compareTo.equals(getHash());
}
}

View File

@ -1,48 +0,0 @@
package link.infra.packwiz.installer.metadata.hash;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public abstract class Hash {
protected abstract String getStringValue();
protected abstract String getType();
public static class TypeHandler implements JsonDeserializer<Hash>, JsonSerializer<Hash> {
@Override
public JsonElement serialize(Hash src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject out = new JsonObject();
out.add("type", new JsonPrimitive(src.getType()));
out.add("value", new JsonPrimitive(src.getStringValue()));
return out;
}
@Override
public Hash deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject obj = json.getAsJsonObject();
String type, value;
try {
type = obj.get("type").getAsString();
value = obj.get("value").getAsString();
} catch (NullPointerException e) {
throw new JsonParseException("Invalid hash JSON data");
}
try {
return HashUtils.getHash(type, value);
} catch (Exception e) {
throw new JsonParseException("Failed to create hash object", e);
}
}
}
}

View File

@ -1,29 +0,0 @@
package link.infra.packwiz.installer.metadata.hash;
import java.util.HashMap;
import java.util.Map;
public class HashUtils {
private static final Map<String, IHasher> hashTypeConversion = new HashMap<String, IHasher>();
static {
hashTypeConversion.put("sha256", new HashingSourceHasher("sha256"));
hashTypeConversion.put("murmur2", new Murmur2Hasher());
}
public static IHasher getHasher(String type) throws Exception {
IHasher hasher = hashTypeConversion.get(type);
if (hasher == null) {
throw new Exception("Hash type not supported: " + type);
}
return hasher;
}
public static Hash getHash(String type, String value) throws Exception {
if (hashTypeConversion.containsKey(type)) {
return hashTypeConversion.get(type).getHash(value);
}
throw new Exception("Hash type not supported: " + type);
}
}

View File

@ -1,89 +0,0 @@
package link.infra.packwiz.installer.metadata.hash;
import okio.HashingSource;
import okio.Source;
public class HashingSourceHasher implements IHasher {
private String type;
HashingSourceHasher(String type) {
this.type = type;
}
// i love naming things
private class HashingSourceGeneralHashingSource extends GeneralHashingSource {
HashingSource delegateHashing;
HashingSourceHash value;
HashingSourceGeneralHashingSource(HashingSource delegate) {
super(delegate);
delegateHashing = delegate;
}
@Override
public Hash getHash() {
if (value == null) {
value = new HashingSourceHash(delegateHashing.hash().hex());
}
return value;
}
}
// this some funky inner class stuff
// each of these classes is specific to the instance of the HasherHashingSource
// therefore HashingSourceHashes from different parent instances will be not instanceof each other
private class HashingSourceHash extends Hash {
String value;
private HashingSourceHash(String value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof HashingSourceHash)) {
return false;
}
HashingSourceHash objHash = (HashingSourceHash) obj;
if (value != null) {
return value.equalsIgnoreCase(objHash.value);
} else {
return objHash.value == null;
}
}
@Override
public String toString() {
return type + ": " + value;
}
@Override
protected String getStringValue() {
return value;
}
@Override
protected String getType() {
return type;
}
}
@Override
public GeneralHashingSource getHashingSource(Source delegate) {
switch (type) {
case "md5":
return new HashingSourceGeneralHashingSource(HashingSource.md5(delegate));
case "sha256":
return new HashingSourceGeneralHashingSource(HashingSource.sha256(delegate));
case "sha512":
return new HashingSourceGeneralHashingSource(HashingSource.sha512(delegate));
}
throw new RuntimeException("Invalid hash type provided");
}
@Override
public Hash getHash(String value) {
return new HashingSourceHash(value);
}
}

View File

@ -1,8 +0,0 @@
package link.infra.packwiz.installer.metadata.hash;
import okio.Source;
public interface IHasher {
public GeneralHashingSource getHashingSource(Source delegate);
public Hash getHash(String value);
}

View File

@ -1,103 +0,0 @@
package link.infra.packwiz.installer.metadata.hash;
import okio.Buffer;
import okio.Source;
import java.io.IOException;
public class Murmur2Hasher implements IHasher {
private class Murmur2GeneralHashingSource extends GeneralHashingSource {
Murmur2Hash value;
Buffer internalBuffer = new Buffer();
Buffer tempBuffer = new Buffer();
Source delegate;
public Murmur2GeneralHashingSource(Source delegate) {
super(delegate);
this.delegate = delegate;
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long out = delegate.read(tempBuffer, byteCount);
if (out > -1) {
sink.write(tempBuffer.clone(), out);
internalBuffer.write(tempBuffer, out);
}
return out;
}
@Override
public Hash getHash() {
if (value == null) {
byte[] data = computeNormalizedArray(internalBuffer.readByteArray());
value = new Murmur2Hash(Murmur2Lib.hash32(data, data.length, 1));
}
return value;
}
// Credit to https://github.com/modmuss50/CAV2/blob/master/murmur.go
private byte[] computeNormalizedArray(byte[] input) {
byte[] output = new byte[input.length];
int num = 0;
for (byte b : input) {
if (!(b == 9 || b == 10 || b == 13 || b == 32)) {
output[num] = b;
num++;
}
}
byte[] outputTrimmed = new byte[num];
System.arraycopy(output, 0, outputTrimmed, 0, num);
return outputTrimmed;
}
}
private static class Murmur2Hash extends Hash {
int value;
private Murmur2Hash(String value) {
// Parsing as long then casting to int converts values gt int max value but lt uint max value
// into negatives. I presume this is how the murmur2 code handles this.
this.value = (int)Long.parseLong(value);
}
private Murmur2Hash(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Murmur2Hash)) {
return false;
}
Murmur2Hash objHash = (Murmur2Hash) obj;
return value == objHash.value;
}
@Override
public String toString() {
return "murmur2: " + value;
}
@Override
protected String getStringValue() {
return Integer.toString(value);
}
@Override
protected String getType() {
return "murmur2";
}
}
@Override
public GeneralHashingSource getHashingSource(Source delegate) {
return new Murmur2GeneralHashingSource(delegate);
}
@Override
public Hash getHash(String value) {
return new Murmur2Hash(value);
}
}

View File

@ -0,0 +1,10 @@
package link.infra.packwiz.installer.metadata.hash
import okio.ForwardingSource
import okio.Source
abstract class GeneralHashingSource(delegate: Source) : ForwardingSource(delegate) {
abstract val hash: Hash
fun hashIsEqual(compareTo: Any) = compareTo == hash
}

View File

@ -0,0 +1,34 @@
package link.infra.packwiz.installer.metadata.hash
import com.google.gson.*
import java.lang.reflect.Type
abstract class Hash {
protected abstract val stringValue: String
protected abstract val type: String
class TypeHandler : JsonDeserializer<Hash>, JsonSerializer<Hash> {
override fun serialize(src: Hash, typeOfSrc: Type, context: JsonSerializationContext): JsonElement = JsonObject().apply {
add("type", JsonPrimitive(src.type))
add("value", JsonPrimitive(src.stringValue))
}
@Throws(JsonParseException::class)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Hash {
val obj = json.asJsonObject
val type: String
val value: String
try {
type = obj["type"].asString
value = obj["value"].asString
} catch (e: NullPointerException) {
throw JsonParseException("Invalid hash JSON data")
}
return try {
HashUtils.getHash(type, value)
} catch (e: Exception) {
throw JsonParseException("Failed to create hash object", e)
}
}
}
}

View File

@ -0,0 +1,21 @@
package link.infra.packwiz.installer.metadata.hash
object HashUtils {
private val hashTypeConversion: Map<String, IHasher> = mapOf(
"sha256" to HashingSourceHasher("sha256"),
"sha512" to HashingSourceHasher("sha512"),
"murmur2" to Murmur2Hasher()
)
@JvmStatic
@Throws(Exception::class)
fun getHasher(type: String): IHasher {
return hashTypeConversion[type] ?: throw Exception("Hash type not supported: $type")
}
@JvmStatic
@Throws(Exception::class)
fun getHash(type: String, value: String): Hash {
return hashTypeConversion[type]?.getHash(value) ?: throw Exception("Hash type not supported: $type")
}
}

View File

@ -0,0 +1,45 @@
package link.infra.packwiz.installer.metadata.hash
import okio.HashingSource
import okio.Source
class HashingSourceHasher internal constructor(private val type: String) : IHasher {
// i love naming things
private inner class HashingSourceGeneralHashingSource internal constructor(val delegateHashing: HashingSource) : GeneralHashingSource(delegateHashing) {
override val hash: Hash by lazy(LazyThreadSafetyMode.NONE) {
HashingSourceHash(delegateHashing.hash.hex())
}
}
// this some funky inner class stuff
// each of these classes is specific to the instance of the HasherHashingSource
// therefore HashingSourceHashes from different parent instances will be not instanceof each other
private inner class HashingSourceHash(val value: String) : Hash() {
override val stringValue get() = value
override fun equals(other: Any?): Boolean {
if (other !is HashingSourceHash) {
return false
}
return stringValue.equals(other.stringValue, ignoreCase = true)
}
override fun toString(): String = "$type: $stringValue"
override fun hashCode(): Int = value.hashCode()
override val type: String get() = this@HashingSourceHasher.type
}
override fun getHashingSource(delegate: Source): GeneralHashingSource {
when (type) {
"md5" -> return HashingSourceGeneralHashingSource(HashingSource.md5(delegate))
"sha256" -> return HashingSourceGeneralHashingSource(HashingSource.sha256(delegate))
"sha512" -> return HashingSourceGeneralHashingSource(HashingSource.sha512(delegate))
}
throw RuntimeException("Invalid hash type provided")
}
override fun getHash(value: String): Hash {
return HashingSourceHash(value)
}
}

View File

@ -0,0 +1,8 @@
package link.infra.packwiz.installer.metadata.hash
import okio.Source
interface IHasher {
fun getHashingSource(delegate: Source): GeneralHashingSource
fun getHash(value: String): Hash
}

View File

@ -0,0 +1,76 @@
package link.infra.packwiz.installer.metadata.hash
import okio.Buffer
import okio.Source
import java.io.IOException
class Murmur2Hasher : IHasher {
private inner class Murmur2GeneralHashingSource(delegate: Source) : GeneralHashingSource(delegate) {
val internalBuffer = Buffer()
val tempBuffer = Buffer()
override val hash: Hash by lazy(LazyThreadSafetyMode.NONE) {
val data = computeNormalizedArray(internalBuffer.readByteArray())
Murmur2Hash(Murmur2Lib.hash32(data, data.size, 1))
}
@Throws(IOException::class)
override fun read(sink: Buffer, byteCount: Long): Long {
val out = delegate.read(tempBuffer, byteCount)
if (out > -1) {
sink.write(tempBuffer.clone(), out)
internalBuffer.write(tempBuffer, out)
}
return out
}
// Credit to https://github.com/modmuss50/CAV2/blob/master/murmur.go
private fun computeNormalizedArray(input: ByteArray): ByteArray {
val output = ByteArray(input.size)
var index = 0
for (b in input) {
when (b.toInt()) {
9, 10, 13, 32 -> {}
else -> {
output[index] = b
index++
}
}
}
val outputTrimmed = ByteArray(index)
System.arraycopy(output, 0, outputTrimmed, 0, index)
return outputTrimmed
}
}
private class Murmur2Hash : Hash {
val value: Int
constructor(value: String) {
// Parsing as long then casting to int converts values gt int max value but lt uint max value
// into negatives. I presume this is how the murmur2 code handles this.
this.value = value.toLong().toInt()
}
constructor(value: Int) {
this.value = value
}
override val stringValue get() = value.toString()
override val type get() = "murmur2"
override fun equals(other: Any?): Boolean {
if (other !is Murmur2Hash) {
return false
}
return value == other.value
}
override fun toString(): String = "murmur2: $value"
override fun hashCode(): Int = value
}
override fun getHashingSource(delegate: Source): GeneralHashingSource = Murmur2GeneralHashingSource(delegate)
override fun getHash(value: String): Hash = Murmur2Hash(value)
}