mirror of
				https://github.com/packwiz/packwiz-installer.git
				synced 2025-10-31 11:04:31 +01:00 
			
		
		
		
	Port hashing stuff to Kotlin
This commit is contained in:
		| @@ -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()); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| } | ||||
| @@ -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); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| @@ -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); | ||||
| } | ||||
| @@ -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); | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
| @@ -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