Skip to content

Instantly share code, notes, and snippets.

@MrPowerGamerBR
Created May 14, 2025 23:38
Show Gist options
  • Save MrPowerGamerBR/b66c52456154a36a5482149e3140e643 to your computer and use it in GitHub Desktop.
Save MrPowerGamerBR/b66c52456154a36a5482149e3140e643 to your computer and use it in GitHub Desktop.
A port of Discord's experiment hash calculation (ported by Gemini 2.5 Pro with some fixes thx Gemini xoxoxo)
package net.perfectdreams.loritta.morenitta
import kotlinx.io.bytestring.encodeToByteString
import java.nio.ByteBuffer
import java.nio.ByteOrder
// Constants used in the MurmurHash3 algorithm
// These are the 32-bit patterns. Using .toInt() correctly captures their signed representation if needed.
private const val C1_INT: Int = 0xCC9E2D51.toInt() // JavaScript: 3432918353
private const val C2_INT: Int = 0x1B873593.toInt() // JavaScript: 461845907
private const val M_ADD_INT: Int = 0xE6546B64.toInt() // JavaScript: 3864292196 (used in addition)
private const val FMIX_C1_INT: Int = 0x85EBCA6B.toInt() // JavaScript: 2246822507
private const val FMIX_C2_INT: Int = 0xC2B2AE35.toInt() // JavaScript: 3266489909
/**
* Calculates a hash value for the given string, similar to the provided JavaScript function.
*
* @param e The input string.
* @param seed An optional seed value, defaults to 0.
* @return A UInt representing the 32-bit hash value.
*/
fun discordExperimentHash(e: String, seed: Int = 0): UInt {
var h1: Int = seed // This corresponds to '$' in the JavaScript code
// Encode the string to a UTF-8 byte array
val bytes = e.encodeToByteString(Charsets.UTF_8)
val len = bytes.size
val nblocks = len / 4 // Number of 4-byte chunks
// Use ByteBuffer to read 32-bit integers in little-endian order
val buffer = ByteBuffer.wrap(bytes.toByteArray()).order(ByteOrder.LITTLE_ENDIAN)
// --- Process 4-byte chunks ---
for (b in 0 until nblocks) {
val offset = b * 4
var k1: Int = buffer.getInt(offset) // Corresponds to 'c' getting m.getUint32(n, !0)
// k1 = Math.imul(k1, 3432918353)
k1 *= C1_INT
// k1 = (k1 << 15) | (k1 >>> 17)
k1 = (k1 shl 15) or (k1 ushr 17) // 'ushr' is the logical right shift (>>>)
// k1 = Math.imul(k1, 461845907)
k1 *= C2_INT
// h1 ^= k1
h1 = h1 xor k1
// h1 = (h1 << 13) | (h1 >>> 19)
h1 = (h1 shl 13) or (h1 ushr 19)
// h1 = Math.imul(h1, 5) + 3864292196
// Kotlin's Int arithmetic wraps, matching Math.imul and 32-bit addition
h1 = h1 * 5 + M_ADD_INT
// ($ >>>= 0) in JS is implicit in Kotlin's Int type for bitwise ops
}
// --- Process remaining tail bytes ---
var tailK1: Int = 0 // Corresponds to 'c' for the tail part
val tailIndex = nblocks * 4
val remainder = len and 3 // Equivalent to len % 4 or (3 & u) in JS
// Mimic JavaScript's switch fallthrough for tail byte processing
if (remainder >= 3) { // case 3
// c ^= t[f + 2] << 16
tailK1 = tailK1 xor ((bytes[tailIndex + 2].toInt() and 0xFF) shl 16)
}
if (remainder >= 2) { // case 2 (or fallthrough from 3)
// c ^= t[f + 1] << 8
tailK1 = tailK1 xor ((bytes[tailIndex + 1].toInt() and 0xFF) shl 8)
}
if (remainder >= 1) { // case 1 (or fallthrough from 2)
// c ^= t[f + 0]
tailK1 = tailK1 xor (bytes[tailIndex + 0].toInt() and 0xFF)
// Process tailK1 as in JS:
// c = Math.imul(c, 3432918353)
// c = (c << 15) | (c >>> 17)
// c = Math.imul(c, 461845907)
// $ ^= c
tailK1 *= C1_INT
tailK1 = (tailK1 shl 15) or (tailK1 ushr 17)
tailK1 *= C2_INT
h1 = h1 xor tailK1
}
// --- Finalization (fmix32) ---
// h1 ^= len
h1 = h1 xor len
// h1 ^= h1 >>> 16
h1 = h1 xor (h1 ushr 16)
// h1 = Math.imul(h1, 2246822507)
h1 *= FMIX_C1_INT
// h1 ^= h1 >>> 13
h1 = h1 xor (h1 ushr 13)
// h1 = Math.imul(h1, 3266489909)
h1 *= FMIX_C2_INT
// h1 ^= h1 >>> 16
h1 = h1 xor (h1 ushr 16)
// The final `>>> 0` in JavaScript effectively converts the 32-bit signed result
// (which is how JS bitwise operations treat numbers) into an unsigned 32-bit number.
// In Kotlin, we achieve this by converting the Int (which holds the correct 32-bit pattern)
// to a UInt.
return h1.toUInt()
}
fun main() {
println(discordExperimentHash("MrPowerGamerBR").toInt() % 10_000)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment