PeachScript.kt

1.9 KB · raw

1package com.peachpasswords.vault23/**4 * PeachScript outer codec — maps each payload byte to one Private-Use-Area code5 * point in U+E000..U+E0FF, and back. Byte-for-byte the browser's `peachScript.ts`:6 * byte 0 → U+E000, byte 255 → U+E0FF. The bundled PeachScript font renders those7 * 256 code points; outside Peach the string is unreadable boxes (intended).8 */9object PeachScript {10    private const val BASE = 0xE0001112    /** Encode binary [bytes] to a PeachScript string (one code point per byte). */13    fun encode(bytes: ByteArray): String {14        val sb = StringBuilder(bytes.size)15        for (b in bytes) sb.appendCodePoint(BASE + (b.toInt() and 0xff))16        return sb.toString()17    }1819    /**20     * Decode a PeachScript string back to bytes. Throws on any code point outside21     * U+E000..U+E0FF (edited / truncated / not PeachScript), matching the browser's22     * INVALID_PEACHSCRIPT_CHARACTER.23     */24    fun decode(str: String): ByteArray {25        val cps = str.codePoints().toArray()26        val out = ByteArray(cps.size)27        for (i in cps.indices) {28            val cp = cps[i]29            require(cp in BASE..(BASE + 0xff)) { "INVALID_PEACHSCRIPT_CHARACTER" }30            out[i] = (cp - BASE).toByte()31        }32        return out33    }3435    /** Strip all whitespace before decoding (browser `normalisePeachScript`). */36    fun normalise(str: String): String = str.replace(Regex("\\s+"), "")3738    /**39     * Cheap pre-flight: non-empty and every non-whitespace char is in the PUA40     * range. Used to gate the import button without throwing.41     */42    fun isPeachScript(str: String): Boolean {43        val t = normalise(str)44        if (t.isEmpty()) return false45        var i = 046        while (i < t.length) {47            val cp = t.codePointAt(i)48            if (cp < BASE || cp > BASE + 0xff) return false49            i += Character.charCount(cp)50        }51        return true52    }53}