implement numeric encodings, optimize
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
package com.infendro.encoding.stream
|
||||
|
||||
private val alphabet = "0123456789ABCDEF".encodeToByteArray()
|
||||
|
||||
object Base16 : StreamEncoding(alphabet)
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.infendro.encoding.stream
|
||||
|
||||
private val alphabet = "01".encodeToByteArray()
|
||||
|
||||
object Base2 : StreamEncoding(alphabet)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.infendro.encoding.stream
|
||||
|
||||
private val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".encodeToByteArray()
|
||||
private const val padding = '='.code.toByte()
|
||||
|
||||
object Base32 : StreamEncoding(alphabet, padding)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.infendro.encoding.stream
|
||||
|
||||
private val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".encodeToByteArray()
|
||||
private const val padding = '='.code.toByte()
|
||||
|
||||
object Base64 : StreamEncoding(alphabet, padding)
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.infendro.encoding.stream
|
||||
|
||||
private val alphabet = "01234567".encodeToByteArray()
|
||||
|
||||
object Base8 : StreamEncoding(alphabet)
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.infendro.encoding.stream
|
||||
|
||||
import com.infendro.encoding.Encoding
|
||||
import com.infendro.encoding.util.align
|
||||
import com.infendro.encoding.util.divCeil
|
||||
import com.infendro.encoding.util.lcm
|
||||
import com.infendro.encoding.util.log2
|
||||
|
||||
open class StreamEncoding(
|
||||
alphabet: ByteArray,
|
||||
private val padding: Byte? = null,
|
||||
) : Encoding(alphabet) {
|
||||
init {
|
||||
require(base % 2 == 0)
|
||||
}
|
||||
|
||||
override fun encode(bytes: ByteArray): ByteArray {
|
||||
val bits = log2(base).toInt()
|
||||
val size = (bytes.size * 8).divCeil(bits)
|
||||
val encodedSize = when (padding) {
|
||||
null -> size
|
||||
else -> {
|
||||
val block = lcm(bits, 8) / bits
|
||||
size.align(block)
|
||||
}
|
||||
}
|
||||
val result = ByteArray(encodedSize)
|
||||
|
||||
var r = 0
|
||||
var buffer = 0
|
||||
var bufferBits = 0
|
||||
|
||||
for (i in bytes.indices) {
|
||||
buffer = (buffer shl 8) + bytes[i]
|
||||
bufferBits += 8
|
||||
|
||||
while (bufferBits >= bits) {
|
||||
val offset = bufferBits - bits
|
||||
val index = (buffer ushr offset)
|
||||
buffer = buffer and ((base - 1) shl offset).inv()
|
||||
bufferBits -= bits
|
||||
|
||||
result[r++] = alphabet[index]
|
||||
}
|
||||
}
|
||||
if (bufferBits > 0) {
|
||||
buffer = buffer shl (bits - bufferBits)
|
||||
val index = buffer
|
||||
result[r++] = alphabet[index]
|
||||
}
|
||||
|
||||
if (padding != null) {
|
||||
while (r < result.size) {
|
||||
result[r++] = padding
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun decode(bytes: ByteArray): ByteArray {
|
||||
val bits = log2(base).toInt()
|
||||
val index = padding?.let { bytes.indexOf(it) } ?: -1
|
||||
val decodedSize = when (index) {
|
||||
-1 -> bytes.size * bits / 8
|
||||
else -> index * bits / 8
|
||||
}
|
||||
val result = ByteArray(decodedSize)
|
||||
|
||||
var r = 0
|
||||
var buffer = 0
|
||||
var bufferBits = 0
|
||||
|
||||
for (i in bytes.indices) {
|
||||
val byte = bytes[i]
|
||||
if (byte == padding) break
|
||||
|
||||
val index = alphabet.indexOf(byte)
|
||||
if (index == -1) throw Exception() //TODO
|
||||
|
||||
buffer = (buffer shl bits) + index.toByte()
|
||||
bufferBits += bits
|
||||
|
||||
if (bufferBits >= 8) {
|
||||
val offset = bufferBits - 8
|
||||
val character = (buffer ushr offset).toByte()
|
||||
buffer = buffer and (0xFF shl offset).inv()
|
||||
bufferBits -= 8
|
||||
|
||||
result[r++] = character
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user