How to encode and decode any string into Base‑36 in Kotlin

1 Answer

0 votes
import java.math.BigInteger
import java.nio.charset.StandardCharsets

/**
 * Encodes a text string into its arbitrary-precision Base-36 string equivalent.
 * * Algorithm:
 * 1. Convert the input string into a raw byte array (base-256).
 * 2. Parse the bytes as a positive BigInteger using signum = 1 (to avoid negative interpretation).
 * 3. Convert the BigInteger directly to a Base-36 string representation via standard radix translation.
 */
fun encodeToBase36(text: String): String {
    if (text.isEmpty()) return ""
    
    // Convert string to bytes safely using UTF-8
    val bytes = text.toByteArray(StandardCharsets.UTF_8)
    
    // Construct a positive BigInteger out of the byte array magnitude
    val bigInt = BigInteger(1, bytes)
    
    // BigInteger natively handles radix conversion up to base 36
    return bigInt.toString(36).uppercase()
}

/**
 * Decodes a Base-36 encoded string back into its original text representation.
 * * Algorithm:
 * 1. Parse the Base-36 string into a BigInteger specifying radix 36.
 * 2. Extract the underlying raw big-endian byte array.
 * 3. Safely strip off any leading padding byte (0x00) which BigInteger sometimes appends to keep the sign bit positive.
 */
fun decodeFromBase36(encoded: String): String {
    if (encoded.isEmpty()) return ""
    
    // Parse the alphanumeric base-36 string back into a BigInteger
    val bigInt = BigInteger(encoded, 36)
    
    // toByteArray() returns a signed big-endian two's-complement byte array
    val rawBytes = bigInt.toByteArray()
    
    // If the leading byte is zero and it's not a standalone zero byte, 
    // it's an artificial sign-padding byte introduced by BigInteger. We slice it off.
    val cleanBytes = if (rawBytes.isNotEmpty() && rawBytes[0] == 0.toByte() && rawBytes.size > 1) {
        rawBytes.copyOfRange(1, rawBytes.size)
    } else {
        rawBytes
    }
    
    return String(cleanBytes, StandardCharsets.UTF_8)
}

fun main() {
    val text = "Hello Universe!"
    val encoded = encodeToBase36(text)
    val decoded = decodeFromBase36(encoded)

    println("Original: $text")
    println("Base-36 encoded: $encoded")
    println("Decoded: $decoded")
}


/*
run:

Original: Hello Universe!
Base-36 encoded: LP4N024HJ1YVBVD84Y8TYQP
Decoded: Hello Universe!

*/

 



answered Jul 7 by avibootz
...