From ecaab219c23e882519d12fd0967a9fc189094fc8 Mon Sep 17 00:00:00 2001 From: comp500 Date: Thu, 19 Dec 2019 14:58:07 +0000 Subject: [PATCH] Port hashing stuff to Kotlin --- .../metadata/hash/GeneralHashingSource.java | 18 --- .../packwiz/installer/metadata/hash/Hash.java | 48 -------- .../installer/metadata/hash/HashUtils.java | 29 ----- .../metadata/hash/HashingSourceHasher.java | 89 --------------- .../installer/metadata/hash/IHasher.java | 8 -- .../metadata/hash/Murmur2Hasher.java | 103 ------------------ .../metadata/hash/GeneralHashingSource.kt | 10 ++ .../packwiz/installer/metadata/hash/Hash.kt | 34 ++++++ .../installer/metadata/hash/HashUtils.kt | 21 ++++ .../metadata/hash/HashingSourceHasher.kt | 45 ++++++++ .../installer/metadata/hash/IHasher.kt | 8 ++ .../installer/metadata/hash/Murmur2Hasher.kt | 76 +++++++++++++ 12 files changed, 194 insertions(+), 295 deletions(-) delete mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.java delete mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java delete mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/HashUtils.java delete mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java delete mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java delete mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java create mode 100644 src/main/kotlin/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.kt create mode 100644 src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Hash.kt create mode 100644 src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashUtils.kt create mode 100644 src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.kt create mode 100644 src/main/kotlin/link/infra/packwiz/installer/metadata/hash/IHasher.kt create mode 100644 src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.kt diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.java deleted file mode 100644 index 6ac9514..0000000 --- a/src/main/java/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.java +++ /dev/null @@ -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()); - } - -} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java deleted file mode 100644 index 0f7eed3..0000000 --- a/src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java +++ /dev/null @@ -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, JsonSerializer { - - @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); - } - } - - } -} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/HashUtils.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/HashUtils.java deleted file mode 100644 index 47913e2..0000000 --- a/src/main/java/link/infra/packwiz/installer/metadata/hash/HashUtils.java +++ /dev/null @@ -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 hashTypeConversion = new HashMap(); - 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); - } - -} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java deleted file mode 100644 index f973e9e..0000000 --- a/src/main/java/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.java +++ /dev/null @@ -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); - } - -} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java deleted file mode 100644 index 921705e..0000000 --- a/src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java deleted file mode 100644 index 0964c0b..0000000 --- a/src/main/java/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.java +++ /dev/null @@ -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); - } - -} \ No newline at end of file diff --git a/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.kt b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.kt new file mode 100644 index 0000000..bc6aeac --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/GeneralHashingSource.kt @@ -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 +} \ No newline at end of file diff --git a/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Hash.kt b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Hash.kt new file mode 100644 index 0000000..1bcf6c1 --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Hash.kt @@ -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, JsonSerializer { + 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) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashUtils.kt b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashUtils.kt new file mode 100644 index 0000000..955cc8c --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashUtils.kt @@ -0,0 +1,21 @@ +package link.infra.packwiz.installer.metadata.hash + +object HashUtils { + private val hashTypeConversion: Map = 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") + } +} \ No newline at end of file diff --git a/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.kt b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.kt new file mode 100644 index 0000000..15f79e4 --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/HashingSourceHasher.kt @@ -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) + } +} \ No newline at end of file diff --git a/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/IHasher.kt b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/IHasher.kt new file mode 100644 index 0000000..0aa223a --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/IHasher.kt @@ -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 +} \ No newline at end of file diff --git a/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.kt b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.kt new file mode 100644 index 0000000..62cc6c7 --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/metadata/hash/Murmur2Hasher.kt @@ -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) +} \ No newline at end of file