Skip to content

Commit

Permalink
✨ Support base32 and more base coding (#61)
Browse files Browse the repository at this point in the history
* ⬆️ Upgrade sisyphus plugin to 0.0.6-M2

* ✨ Support base32 and more base coding

* 🚨 Make ktlint happy
  • Loading branch information
devkanro authored Jul 31, 2020
1 parent 25847f3 commit 224db19
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 95 deletions.
4 changes: 2 additions & 2 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ plugins {
`java-library`
`kotlin-dsl`
id("idea")
id("com.bybutter.sisyphus.project") version "0.0.6-M0"
id("com.bybutter.sisyphus.project") version "0.0.6-M2"
}

dependencies {
implementation(platform("com.bybutter.sisyphus:sisyphus-dependencies:0.0.6-M0"))
implementation(platform("com.bybutter.sisyphus:sisyphus-dependencies:0.0.6-M2"))
implementation("com.bybutter.sisyphus.tools:sisyphus-protobuf-gradle-plugin")
implementation("com.bybutter.sisyphus.tools:sisyphus-project-gradle-plugin")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.bybutter.sisyphus.data

import java.io.ByteArrayOutputStream
import java.io.InputStream
import kotlin.math.log2
import kotlin.math.truncate

open class BaseEncoding(val table: CharArray) {
val bits: Int
val base = table.size
private val reverseMap: Map<Char, Int> = table.mapIndexed { index, char ->
char to index
}.associate { it }

init {
val bits = log2(base.toDouble())
if (bits - truncate(bits) != 0.0) {
throw IllegalArgumentException("Size of char table must be integer power of 2.")
}
this.bits = bits.toInt()
}

open fun encode(input: ByteArray): String {
return encode(input.inputStream())
}

open fun encode(input: InputStream): String = buildString {
val stream = BitInputStream(input)
val data = IntArray(1)

do {
data[0] = 0
val read = stream.readInt(data, bits)
if (read > 0) append(table[data[0]])
} while (read == bits)
}

open fun decode(input: String): ByteArray {
val output = ByteArrayOutputStream()
val stream = BitOutputStream(output)

for (char in input) {
stream.writeInt(reverseMap[char]
?: throw IllegalArgumentException("Wrong base$bits input '$char'."), bits)
}
return output.toByteArray()
}

companion object {
val base64 = BaseEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray())

val base64Url = BaseEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray())

val base32 = BaseEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".toCharArray())

val base32Hex = BaseEncoding("0123456789ABCDEFGHIJKLMNOPQRSTUV".toCharArray())
}
}
Original file line number Diff line number Diff line change
@@ -1,53 +1,62 @@
package com.bybutter.sisyphus.data

import java.io.InputStream
import kotlin.experimental.and
import kotlin.experimental.inv
import kotlin.experimental.or
import java.io.OutputStream
import java.util.BitSet

class BitInputStream(private val source: InputStream) : InputStream() {
private var pos = 8
private var pos = -1
private var byte: Int = 0

override fun read(): Int {
if (pos > 7) {
if (pos < 0) {
byte = source.read()
if (byte == -1) {
return -1
}
pos = 0
pos = 7
}

return if (byte and (1 shl pos++) > 0) {
return if (byte and (1 shl pos--) > 0) {
1
} else {
0
}
}

fun readBits(byteArray: ByteArray, bits: Int): Int {
if (bits > byteArray.size * 8) {
fun readBits(data: BitSet, bits: Int): Int {
if (bits > data.size()) {
throw IllegalArgumentException()
}

var read = 0
for (i in 0 until bits) {
if (pos > 7) {
byte = source.read()
if (byte == -1) {
break
}
pos = 0
loop@for (i in 0 until bits) {
when (read()) {
0 -> data.set(i, false)
1 -> data.set(i, true)
else -> break@loop
}

read++
if (byte and (1 shl pos++) > 0) {
byteArray[i / 8] = byteArray[i / 8] or (1 shl (i % 8)).toByte()
} else {
byteArray[i / 8] = byteArray[i / 8] and (1 shl (i % 8)).toByte().inv()
}
return read
}

fun readInt(value: IntArray, bits: Int): Int {
if (bits > 32) throw IllegalArgumentException("'bits' must less than or equal to 32.")
if (value.isEmpty()) throw IllegalArgumentException("'value' must not be empty.")
var int = 0

var read = 0
loop@for (i in 0 until bits) {
when (read()) {
1 -> int = int or (1 shl (bits - i - 1))
0 -> int
else -> break@loop
}
read++
}

value[0] = int
return read
}

Expand Down Expand Up @@ -80,92 +89,48 @@ class BitInputStream(private val source: InputStream) : InputStream() {
}
}

class BitBuffer(private val data: ByteArray) {
private var pos = 0

fun read(): Int {
if (pos >= data.size * 8) {
return -1
}

return if (data[pos / 8] and (1 shl (pos % 8)).toByte() > 0) {
1
} else {
0
}
}
class BitOutputStream(private val target: OutputStream) : OutputStream() {
private var pos = 7
private var byte: Int = 0

fun write(value: Int): Int {
if (pos >= data.size * 8) {
return 0
override fun write(b: Int) {
if (b > 0) {
byte = byte or (1 shl pos)
}
pos--

if (value > 0) {
data[pos / 8] = data[pos / 8] or (1 shl (pos % 8)).toByte()
} else {
data[pos / 8] = data[pos / 8] and (1 shl (pos % 8)).toByte().inv()
if (pos < 0) {
target.write(byte)
byte = 0
pos = 7
}
pos++
return 1
}

fun readBits(byteArray: ByteArray, bits: Int): Int {
if (bits > byteArray.size * 8) {
fun writeBits(data: BitSet, bits: Int) {
if (bits > data.size()) {
throw IllegalArgumentException()
}

var read = 0
for (i in 0 until bits) {
if (pos >= data.size * 8) {
break
}

read++
if (data[pos / 8] and (1 shl (pos % 8)).toByte() > 0) {
byteArray[i / 8] = byteArray[i / 8] or (1 shl (i % 8)).toByte()
if (data[i]) {
write(1)
} else {
byteArray[i / 8] = byteArray[i / 8] and (1 shl (i % 8)).toByte().inv()
write(0)
}
pos++
}

return read
}

fun writeBits(byteArray: ByteArray, bits: Int): Int {
if (bits > byteArray.size * 8) {
throw IllegalArgumentException()
}

var written = 0
fun writeInt(value: Int, bits: Int) {
for (i in 0 until bits) {
if (pos >= data.size * 8) {
break
}

written++
if (byteArray[i / 8] and (1 shl (i % 8)).toByte() > 0) {
data[pos / 8] = data[pos / 8] or (1 shl (pos % 8)).toByte()
if (value and (1 shl (bits - i - 1)) > 0) {
write(1)
} else {
data[pos / 8] = data[pos / 8] and (1 shl (pos % 8)).toByte().inv()
write(0)
}
pos++
}

return written
}

fun seek(offset: Int): Int {
pos += offset
if (pos < 0) {
pos = 0
} else if (pos > data.size * 8) {
pos = data.size * 8
}
return pos
}

fun toByteArray(): ByteArray {
return data
override fun close() {
target.close()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,41 +64,84 @@ fun ByteArray.toByte(): Byte {

fun ByteArray.toShort(): Short {
return ByteBuffer.allocate(2).apply {
put(this@toShort)
put(this@toShort.wrapTo(2))
}.getShort(0)
}

fun ByteArray.toInt(): Int {
return ByteBuffer.allocate(4).apply {
put(this@toInt)
put(this@toInt.wrapTo(4))
}.getInt(0)
}

fun ByteArray.toLong(): Long {
return ByteBuffer.allocate(8).apply {
put(this@toLong)
put(this@toLong.wrapTo(8))
}.getLong(0)
}

fun ByteArray.toFloat(): Float {
return ByteBuffer.allocate(4).apply {
put(this@toFloat)
put(this@toFloat.wrapTo(4))
}.getFloat(0)
}

fun ByteArray.toDouble(): Double {
return ByteBuffer.allocate(8).apply {
put(this@toDouble)
put(this@toDouble.wrapTo(8))
}.getDouble(0)
}

fun ByteArray.wrapTo(size: Int): ByteArray {
if (this.size < size) {
return ByteArray(size - this.size) + this
val result = ByteArray(size)
this.copyInto(result, size - this.size)
return result
}
return this
}

inline fun ByteArray.trim(predicate: (Byte) -> Boolean): ByteArray {
var startIndex = 0
var endIndex = size - 1
var startFound = false

while (startIndex <= endIndex) {
val index = if (!startFound) startIndex else endIndex
val match = predicate(this[index])

if (!startFound) {
if (!match)
startFound = true
else
startIndex += 1
} else {
if (!match)
break
else
endIndex -= 1
}
}

return copyOfRange(startIndex, endIndex + 1)
}

inline fun ByteArray.trimStart(predicate: (Byte) -> Boolean): ByteArray {
for (index in this.indices)
if (!predicate(this[index]))
return copyOfRange(index, size)

return byteArrayOf()
}

inline fun ByteArray.trimEnd(predicate: (Byte) -> Boolean): ByteArray {
for (index in this.indices.reversed())
if (!predicate(this[index]))
return copyOfRange(0, index + 1)

return byteArrayOf()
}

class ByteArrayHashingWrapper(val target: ByteArray) {
override fun hashCode(): Int {
return target.hash()
Expand Down
Empty file.
Empty file.

0 comments on commit 224db19

Please sign in to comment.