mirror of
				https://github.com/packwiz/packwiz-installer.git
				synced 2025-11-04 12:34:31 +01:00 
			
		
		
		
	Port hashing stuff to Kotlin
This commit is contained in:
		@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -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")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user