Compare commits
19 Commits
e40ef3168d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
841bdfa21e
|
|||
|
e08cbb0fa9
|
|||
|
61d6a8e191
|
|||
|
6283c2f44d
|
|||
|
8290955755
|
|||
|
c1b8800e82
|
|||
|
684b6d78ea
|
|||
|
24c667bc9c
|
|||
|
a347bd617c
|
|||
|
a553aa6723
|
|||
|
83fd77c8ee
|
|||
|
456f33f2d9
|
|||
|
55faf1d670
|
|||
|
45d74d9cff
|
|||
|
688beb61b9
|
|||
|
21858641e5
|
|||
|
704cf2a796
|
|||
| bd5d904a3b | |||
|
843013b7e7
|
@@ -1,36 +0,0 @@
|
|||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
env:
|
|
||||||
GIT__TOKEN: ${{ secrets.TOKEN }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: linux
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/cache/restore@v4
|
|
||||||
with:
|
|
||||||
key: gradle
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches/
|
|
||||||
~/.gradle/wrapper/
|
|
||||||
~/.konan/
|
|
||||||
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: '24'
|
|
||||||
distribution: 'temurin'
|
|
||||||
|
|
||||||
- run: ./gradlew publish
|
|
||||||
|
|
||||||
- uses: actions/cache/save@v4
|
|
||||||
with:
|
|
||||||
key: gradle
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches/
|
|
||||||
~/.gradle/wrapper/
|
|
||||||
~/.konan/
|
|
||||||
47
.gitea/workflows/publish.yaml
Normal file
47
.gitea/workflows/publish.yaml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: linux
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '24'
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: gradle-${{ hashFiles('**/libs.versions.toml') }}
|
||||||
|
restore-keys: gradle
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches/
|
||||||
|
~/.gradle/wrapper/
|
||||||
|
|
||||||
|
- name: Cache Konan
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: konan-${{ hashFiles('**/libs.versions.toml') }}
|
||||||
|
restore-keys: konan
|
||||||
|
path: |
|
||||||
|
~/.konan/
|
||||||
|
|
||||||
|
- name: Cache Node
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: node-${{ hashFiles('**/libs.versions.toml') }}
|
||||||
|
restore-keys: node
|
||||||
|
path: |
|
||||||
|
~/.gradle/nodejs
|
||||||
|
~/.gradle/yarn
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: ./gradlew publish
|
||||||
|
env:
|
||||||
|
INFENDRO__TOKEN: ${{ secrets.TOKEN }}
|
||||||
28
.gitea/workflows/test.yaml
Normal file
28
.gitea/workflows/test.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: linux
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '24'
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Cache Gradle
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
key: gradle-${{ hashFiles('**/libs.versions.toml') }}
|
||||||
|
restore-keys: gradle
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches/
|
||||||
|
~/.gradle/wrapper/
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ./gradlew jvmTest
|
||||||
35
README.md
35
README.md
@@ -1,18 +1,19 @@
|
|||||||
# Kotlin Encoding
|
# encoding-kt
|
||||||
|
|
||||||
This library provides various character encodings in Kotlin Multiplatform.
|
`encoding-kt` is a Kotlin Multiplatform library that provides common `ByteArray` encodings.
|
||||||
|
|
||||||
## Features
|
Supported encodings:
|
||||||
|
- Base2 (binary)
|
||||||
* Encode and decode ByteArrays using various character encodings.
|
- Base8 (octal)
|
||||||
* Binary
|
- Base10 (decimal)
|
||||||
* Hex
|
- Base16 (hexadecimal, uppercase/lowercase)
|
||||||
* Base32
|
- Base32
|
||||||
* Base64
|
- Base36
|
||||||
* Multiplatform support
|
- Base45
|
||||||
* JVM
|
- Base56
|
||||||
* JavaScript
|
- Base58
|
||||||
* Native (Linux)
|
- Base62
|
||||||
|
- Base64 (standard/URL)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -24,22 +25,20 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.infendro:encoding:1.1.0")
|
implementation("com.infendro:encoding:1.3.0")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
import com.infendro.encoding.Hex
|
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
// the string to encode
|
// the string to encode
|
||||||
val test = "Hello World!".encodeToByteArray()
|
val test = "Hello World!".encodeToByteArray()
|
||||||
|
|
||||||
// encode the string with the desired encoding
|
// encode the string with the desired encoding
|
||||||
val encoded = Hex.encode(test).decodeToString()
|
val encoded = Base16.encode(test).decodeToString()
|
||||||
|
|
||||||
println(encoded) // 48656c6c6f20576f726c6421
|
println(encoded) // 48656C6C6F20576F726C6421
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,58 +1,40 @@
|
|||||||
group = "com.infendro"
|
@file:OptIn(ExperimentalWasmDsl::class)
|
||||||
version = "1.2.0"
|
|
||||||
|
|
||||||
repositories {
|
import com.infendro.plugin.infendro
|
||||||
mavenCentral()
|
import com.infendro.plugin.token
|
||||||
}
|
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||||
|
|
||||||
|
group = "com.infendro"
|
||||||
|
version = "1.3.0"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
alias(libs.plugins.infendro)
|
||||||
alias(libs.plugins.multiplatform)
|
alias(libs.plugins.multiplatform)
|
||||||
id("maven-publish")
|
id("maven-publish")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm()
|
||||||
js {
|
|
||||||
nodejs()
|
|
||||||
}
|
|
||||||
linuxX64()
|
linuxX64()
|
||||||
|
linuxArm64()
|
||||||
|
mingwX64()
|
||||||
|
macosX64()
|
||||||
|
macosArm64()
|
||||||
|
js { nodejs() }
|
||||||
|
wasmJs { nodejs() }
|
||||||
|
wasmWasi { nodejs() }
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonTest.dependencies {
|
commonTest.dependencies {
|
||||||
implementation(libs.test)
|
implementation(libs.test)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compilerOptions {
|
|
||||||
freeCompilerArgs.add("-opt-in=kotlin.ExperimentalUnsignedTypes")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing.repositories {
|
||||||
repositories {
|
infendro(token)
|
||||||
maven {
|
|
||||||
url = uri("https://git.infendro.com/api/packages/Infendro/maven")
|
|
||||||
authentication.create("header", HttpHeaderAuthentication::class)
|
|
||||||
credentials(HttpHeaderCredentials::class) {
|
|
||||||
val token = secret("git.token") ?: throw GradleException()
|
|
||||||
|
|
||||||
name = "Authorization"
|
|
||||||
value = "token $token"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun secret(
|
|
||||||
key: String,
|
|
||||||
): String? {
|
|
||||||
val property = key
|
|
||||||
val environment = key
|
|
||||||
.uppercase()
|
|
||||||
.replace(".", "__")
|
|
||||||
|
|
||||||
val prop = properties[property] as String?
|
|
||||||
val env = System.getenv(environment)
|
|
||||||
|
|
||||||
return prop ?: env
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin = "2.1.20"
|
infendro = "1.0.0"
|
||||||
|
kotlin = "2.3.0"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
infendro = { id = "com.infendro.plugin", version.ref = "infendro" }
|
||||||
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|||||||
@@ -1 +1,6 @@
|
|||||||
rootProject.name = "encoding"
|
rootProject.name = "encoding"
|
||||||
|
|
||||||
|
pluginManagement.repositories {
|
||||||
|
maven("https://git.infendro.com/api/packages/Infendro/maven")
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|||||||
5
src/commonMain/kotlin/com/infendro/encoding/Base10.kt
Normal file
5
src/commonMain/kotlin/com/infendro/encoding/Base10.kt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
private val alphabet = "0123456789".encodeToByteArray()
|
||||||
|
|
||||||
|
object Base10 : Encoding by NumericEncoding(alphabet)
|
||||||
@@ -1,16 +1,10 @@
|
|||||||
package com.infendro.encoding
|
package com.infendro.encoding
|
||||||
|
|
||||||
object Base16 : Encoding() {
|
private val uppercase = "0123456789ABCDEF".encodeToByteArray()
|
||||||
private val CHARACTERS = ubyteArrayOf(
|
private val lowercase = "0123456789abcdef".encodeToByteArray()
|
||||||
0x30U, 0x31U, 0x32U, 0x33U, 0x34U, 0x35U, 0x36U, 0x37U,
|
|
||||||
0x38U, 0x39U, 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun encode(
|
object Base16 : Encoding by StreamEncoding(uppercase) {
|
||||||
bytes: UByteArray,
|
val UPPERCASE = this
|
||||||
) = encode(bytes, CHARACTERS)
|
|
||||||
|
|
||||||
override fun decode(
|
object LOWERCASE : Encoding by StreamEncoding(lowercase)
|
||||||
bytes: UByteArray,
|
|
||||||
) = decode(bytes, CHARACTERS)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
package com.infendro.encoding
|
package com.infendro.encoding
|
||||||
|
|
||||||
object Base2 : Encoding() {
|
private val alphabet = "01".encodeToByteArray()
|
||||||
private val CHARACTERS = ubyteArrayOf(
|
|
||||||
0x30U, 0x31U,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun encode(
|
object Base2 : Encoding by StreamEncoding(alphabet)
|
||||||
bytes: UByteArray,
|
|
||||||
) = encode(bytes, CHARACTERS)
|
|
||||||
|
|
||||||
override fun decode(
|
|
||||||
bytes: UByteArray,
|
|
||||||
) = decode(bytes, CHARACTERS)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,19 +1,6 @@
|
|||||||
package com.infendro.encoding
|
package com.infendro.encoding
|
||||||
|
|
||||||
object Base32 : Encoding() {
|
private val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".encodeToByteArray()
|
||||||
private val CHARACTERS = ubyteArrayOf(
|
private const val padding = '='.code.toByte()
|
||||||
0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, 0x48U,
|
|
||||||
0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, 0x50U,
|
|
||||||
0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, 0x58U,
|
|
||||||
0x59U, 0x5aU, 0x32U, 0x33U, 0x34U, 0x35U, 0x36U, 0x37U,
|
|
||||||
)
|
|
||||||
private const val PADDING: UByte = 0x3dU
|
|
||||||
|
|
||||||
override fun encode(
|
object Base32 : Encoding by StreamEncoding(alphabet, padding)
|
||||||
bytes: UByteArray,
|
|
||||||
) = encode(bytes, CHARACTERS, PADDING)
|
|
||||||
|
|
||||||
override fun decode(
|
|
||||||
bytes: UByteArray,
|
|
||||||
) = decode(bytes, CHARACTERS, PADDING)
|
|
||||||
}
|
|
||||||
|
|||||||
5
src/commonMain/kotlin/com/infendro/encoding/Base36.kt
Normal file
5
src/commonMain/kotlin/com/infendro/encoding/Base36.kt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
private val alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".encodeToByteArray()
|
||||||
|
|
||||||
|
object Base36 : Encoding by NumericEncoding(alphabet)
|
||||||
51
src/commonMain/kotlin/com/infendro/encoding/Base45.kt
Normal file
51
src/commonMain/kotlin/com/infendro/encoding/Base45.kt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import com.infendro.encoding.exception.IllegalByteException
|
||||||
|
|
||||||
|
object Base45 : Encoding {
|
||||||
|
private val alphabet: ByteArray = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".encodeToByteArray()
|
||||||
|
private val base: Int = alphabet.size
|
||||||
|
|
||||||
|
override fun encode(bytes: ByteArray): ByteArray {
|
||||||
|
val result = mutableListOf<Byte>()
|
||||||
|
|
||||||
|
for (i in bytes.indices step 2) {
|
||||||
|
val remaining = minOf(bytes.size - i, 2)
|
||||||
|
|
||||||
|
var value = 0
|
||||||
|
for (j in 0..<remaining) {
|
||||||
|
val byte = bytes[i + j]
|
||||||
|
value = (value shl 8) or (byte.toInt() and 0xFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat(remaining + 1) {
|
||||||
|
result += alphabet[value % base]
|
||||||
|
value /= base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decode(bytes: ByteArray): ByteArray {
|
||||||
|
val result = mutableListOf<Byte>()
|
||||||
|
|
||||||
|
for (i in bytes.indices step 3) {
|
||||||
|
val remaining = minOf(bytes.size - i, 3)
|
||||||
|
|
||||||
|
var value = 0
|
||||||
|
for (j in (0..<remaining).reversed()) {
|
||||||
|
val byte = bytes[i + j]
|
||||||
|
val index = alphabet.indexOf(byte)
|
||||||
|
if (index == -1) throw IllegalByteException(byte)
|
||||||
|
value = value * base + index
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j in (0..<(remaining - 1)).reversed()) {
|
||||||
|
result += ((value shr (j * 8)) and 0xFF).toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/commonMain/kotlin/com/infendro/encoding/Base56.kt
Normal file
5
src/commonMain/kotlin/com/infendro/encoding/Base56.kt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
private val alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz".encodeToByteArray()
|
||||||
|
|
||||||
|
object Base56 : Encoding by NumericEncoding(alphabet)
|
||||||
5
src/commonMain/kotlin/com/infendro/encoding/Base58.kt
Normal file
5
src/commonMain/kotlin/com/infendro/encoding/Base58.kt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
private val alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".encodeToByteArray()
|
||||||
|
|
||||||
|
object Base58 : Encoding by NumericEncoding(alphabet)
|
||||||
5
src/commonMain/kotlin/com/infendro/encoding/Base62.kt
Normal file
5
src/commonMain/kotlin/com/infendro/encoding/Base62.kt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
private val alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".encodeToByteArray()
|
||||||
|
|
||||||
|
object Base62 : Encoding by NumericEncoding(alphabet)
|
||||||
@@ -1,23 +1,11 @@
|
|||||||
package com.infendro.encoding
|
package com.infendro.encoding
|
||||||
|
|
||||||
object Base64 : Encoding() {
|
private val standard = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".encodeToByteArray()
|
||||||
private val CHARACTERS = ubyteArrayOf(
|
private val url = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".encodeToByteArray()
|
||||||
0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, 0x48U,
|
private const val padding = '='.code.toByte()
|
||||||
0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, 0x50U,
|
|
||||||
0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, 0x58U,
|
|
||||||
0x59U, 0x5aU, 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U,
|
|
||||||
0x67U, 0x68U, 0x69U, 0x6aU, 0x6bU, 0x6cU, 0x6dU, 0x6eU,
|
|
||||||
0x6fU, 0x70U, 0x71U, 0x72U, 0x73U, 0x74U, 0x75U, 0x76U,
|
|
||||||
0x77U, 0x78U, 0x79U, 0x7aU, 0x30U, 0x31U, 0x32U, 0x33U,
|
|
||||||
0x34U, 0x35U, 0x36U, 0x37U, 0x38U, 0x39U, 0x2bU, 0x2fU,
|
|
||||||
)
|
|
||||||
private const val PADDING: UByte = 0x3dU
|
|
||||||
|
|
||||||
override fun encode(
|
object Base64 : Encoding by StreamEncoding(standard, padding) {
|
||||||
bytes: UByteArray,
|
val STANDARD = this
|
||||||
) = encode(bytes, CHARACTERS, PADDING)
|
|
||||||
|
|
||||||
override fun decode(
|
object URL : Encoding by StreamEncoding(url)
|
||||||
bytes: UByteArray,
|
|
||||||
) = decode(bytes, CHARACTERS, PADDING)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
package com.infendro.encoding
|
package com.infendro.encoding
|
||||||
|
|
||||||
object Base8 : Encoding() {
|
private val alphabet = "01234567".encodeToByteArray()
|
||||||
private val CHARACTERS = ubyteArrayOf(
|
|
||||||
0x30U, 0x31U, 0x32U, 0x33U, 0x34U, 0x35U, 0x36U, 0x37U,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun encode(
|
object Base8 : Encoding by StreamEncoding(alphabet)
|
||||||
bytes: UByteArray,
|
|
||||||
) = encode(bytes, CHARACTERS)
|
|
||||||
|
|
||||||
override fun decode(
|
|
||||||
bytes: UByteArray,
|
|
||||||
) = decode(bytes, CHARACTERS)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,92 +1,6 @@
|
|||||||
package com.infendro.encoding
|
package com.infendro.encoding
|
||||||
|
|
||||||
import com.infendro.encoding.util.lcm
|
interface Encoding {
|
||||||
import com.infendro.encoding.util.log2
|
fun encode(bytes: ByteArray): ByteArray
|
||||||
import kotlin.math.pow
|
fun decode(bytes: ByteArray): ByteArray
|
||||||
|
|
||||||
sealed class Encoding {
|
|
||||||
abstract fun encode(
|
|
||||||
bytes: UByteArray,
|
|
||||||
): UByteArray
|
|
||||||
|
|
||||||
abstract fun decode(
|
|
||||||
bytes: UByteArray,
|
|
||||||
): UByteArray
|
|
||||||
|
|
||||||
protected fun encode(
|
|
||||||
bytes: UByteArray,
|
|
||||||
characters: UByteArray,
|
|
||||||
padding: UByte? = null,
|
|
||||||
): UByteArray {
|
|
||||||
val length = log2(characters.size).toInt()
|
|
||||||
val mask = (2.0.pow(length) - 1).toUInt()
|
|
||||||
|
|
||||||
val encoded = mutableListOf<UByte>()
|
|
||||||
|
|
||||||
var buffer = 0U
|
|
||||||
var bits = 0
|
|
||||||
|
|
||||||
for (byte in bytes) {
|
|
||||||
buffer = (buffer shl 8) + byte
|
|
||||||
bits += 8
|
|
||||||
|
|
||||||
while (bits >= length) {
|
|
||||||
val offset = bits - length
|
|
||||||
val index = (buffer shr offset).toInt()
|
|
||||||
buffer = buffer and (mask shl offset).inv()
|
|
||||||
bits -= length
|
|
||||||
|
|
||||||
encoded.add(characters[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bits > 0) {
|
|
||||||
buffer = buffer shl (length - bits)
|
|
||||||
val index = buffer.toInt()
|
|
||||||
encoded.add(characters[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (padding != null) {
|
|
||||||
val block = lcm(length, 8) / length
|
|
||||||
while (encoded.size % block != 0) {
|
|
||||||
encoded.add(padding)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return encoded.toUByteArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun decode(
|
|
||||||
bytes: UByteArray,
|
|
||||||
characters: UByteArray,
|
|
||||||
padding: UByte? = null,
|
|
||||||
): UByteArray {
|
|
||||||
val length = log2(characters.size).toInt()
|
|
||||||
val mask = 0xffU
|
|
||||||
|
|
||||||
val decoded = mutableListOf<UByte>()
|
|
||||||
|
|
||||||
var buffer = 0U
|
|
||||||
var bits = 0
|
|
||||||
|
|
||||||
for (byte in bytes) {
|
|
||||||
if (byte == padding) break
|
|
||||||
|
|
||||||
val index = characters.indexOf(byte)
|
|
||||||
if (index == -1) throw Exception()
|
|
||||||
|
|
||||||
buffer = (buffer shl length) + index.toUByte()
|
|
||||||
bits += length
|
|
||||||
|
|
||||||
if (bits >= 8) {
|
|
||||||
val offset = bits - 8
|
|
||||||
val character = (buffer shr offset).toUByte()
|
|
||||||
buffer = buffer and (mask shl offset).inv()
|
|
||||||
bits -= 8
|
|
||||||
|
|
||||||
decoded.add(character)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoded.toUByteArray()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import com.infendro.encoding.exception.IllegalByteException
|
||||||
|
import com.infendro.encoding.util.BigInt
|
||||||
|
|
||||||
|
class NumericEncoding internal constructor(
|
||||||
|
private val alphabet: ByteArray,
|
||||||
|
) : Encoding {
|
||||||
|
private val base: Int = alphabet.size
|
||||||
|
|
||||||
|
override fun encode(bytes: ByteArray): ByteArray {
|
||||||
|
if (bytes.isEmpty()) return byteArrayOf()
|
||||||
|
|
||||||
|
val result = mutableListOf<Byte>()
|
||||||
|
|
||||||
|
var x = BigInt.from(bytes)
|
||||||
|
while (!x.isZero()) {
|
||||||
|
val (quotient, remainder) = x.divRem(base)
|
||||||
|
x = quotient
|
||||||
|
result += alphabet[remainder]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.asReversed().toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decode(bytes: ByteArray): ByteArray {
|
||||||
|
if (bytes.isEmpty()) return byteArrayOf()
|
||||||
|
|
||||||
|
var value = BigInt.zero
|
||||||
|
for (i in 0..<bytes.size) {
|
||||||
|
val byte = bytes[i]
|
||||||
|
val index = alphabet.indexOf(byte)
|
||||||
|
if (index == -1) throw IllegalByteException(byte)
|
||||||
|
value = value.timesAdd(base, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.toByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import com.infendro.encoding.exception.IllegalByteException
|
||||||
|
import com.infendro.encoding.util.align
|
||||||
|
import com.infendro.encoding.util.divCeil
|
||||||
|
import com.infendro.encoding.util.lcm
|
||||||
|
import com.infendro.encoding.util.log2
|
||||||
|
|
||||||
|
class StreamEncoding internal constructor(
|
||||||
|
private val alphabet: ByteArray,
|
||||||
|
private val padding: Byte? = null,
|
||||||
|
) : Encoding {
|
||||||
|
private val base: Int = alphabet.size
|
||||||
|
private val bits: Int = log2(base).toInt()
|
||||||
|
private val block: Int = lcm(bits, 8) / bits
|
||||||
|
private val mask: Int = (0x01 shl bits) - 1
|
||||||
|
|
||||||
|
override fun encode(bytes: ByteArray): ByteArray {
|
||||||
|
val size = when (padding) {
|
||||||
|
null -> (bytes.size * 8).divCeil(bits)
|
||||||
|
else -> (bytes.size * 8).divCeil(bits).align(block)
|
||||||
|
}
|
||||||
|
val encoded = ByteArray(size)
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
var buffer = 0
|
||||||
|
var bufferBits = 0
|
||||||
|
for (byte in bytes) {
|
||||||
|
buffer = (buffer shl 8) or (byte.toInt() and 0xFF)
|
||||||
|
bufferBits += 8
|
||||||
|
|
||||||
|
while (bufferBits >= bits) {
|
||||||
|
val index = (buffer shr (bufferBits - bits)) and mask
|
||||||
|
bufferBits -= bits
|
||||||
|
encoded[i++] = alphabet[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bufferBits > 0) {
|
||||||
|
val index = (buffer shl (bits - bufferBits)) and mask
|
||||||
|
encoded[i++] = alphabet[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i < encoded.size) {
|
||||||
|
encoded[i++] = padding!!
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decode(bytes: ByteArray): ByteArray {
|
||||||
|
val end = when (padding) {
|
||||||
|
null -> bytes.size
|
||||||
|
else -> when (val index = bytes.indexOf(padding)) {
|
||||||
|
-1 -> bytes.size
|
||||||
|
else -> index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val size = end * bits / 8
|
||||||
|
val decoded = ByteArray(size)
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
var buffer = 0
|
||||||
|
var bufferBits = 0
|
||||||
|
for (b in 0..<end) {
|
||||||
|
val byte = bytes[b]
|
||||||
|
val index = alphabet.indexOf(byte)
|
||||||
|
if (index == -1) throw IllegalByteException(byte)
|
||||||
|
buffer = (buffer shl bits) or index
|
||||||
|
bufferBits += bits
|
||||||
|
|
||||||
|
if (bufferBits >= 8) {
|
||||||
|
val byte = (buffer shr (bufferBits - 8)) and 0xFF
|
||||||
|
bufferBits -= 8
|
||||||
|
decoded[i++] = byte.toByte()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.infendro.encoding.exception
|
||||||
|
|
||||||
|
abstract class DecodeException : Exception() {
|
||||||
|
abstract override val message: String
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.infendro.encoding.exception
|
||||||
|
|
||||||
|
class IllegalByteException(
|
||||||
|
val byte: Byte,
|
||||||
|
) : DecodeException() {
|
||||||
|
override val message: String
|
||||||
|
get() {
|
||||||
|
val hex = "0x${byte.toHexString().padStart(2, '0')}"
|
||||||
|
val char = "'${byte.toInt().toChar()}'"
|
||||||
|
return "Illegal byte $hex ($char)."
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/commonMain/kotlin/com/infendro/encoding/util/BigInt.kt
Normal file
73
src/commonMain/kotlin/com/infendro/encoding/util/BigInt.kt
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package com.infendro.encoding.util
|
||||||
|
|
||||||
|
internal class BigInt private constructor(
|
||||||
|
val words: IntArray,
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
require(words.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isZero(): Boolean = words.all { it == 0 }
|
||||||
|
|
||||||
|
fun timesAdd(multiplier: Int, addend: Int): BigInt {
|
||||||
|
val product = IntArray(words.size)
|
||||||
|
var carry = addend
|
||||||
|
|
||||||
|
for (i in words.indices) {
|
||||||
|
val current = (words[i].toLong() and 0xFFFFFFFFL) * multiplier + carry
|
||||||
|
product[i] = current.toInt()
|
||||||
|
carry = (current ushr 32).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
carry > 0 -> {
|
||||||
|
val expanded = product.copyOf(product.size + 1)
|
||||||
|
expanded[product.size] = carry
|
||||||
|
return BigInt(expanded)
|
||||||
|
}
|
||||||
|
else -> return BigInt(product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun divRem(divisor: Int): Pair<BigInt, Int> {
|
||||||
|
val quotient = IntArray(words.size)
|
||||||
|
var remainder = 0
|
||||||
|
|
||||||
|
for (i in words.indices.reversed()) {
|
||||||
|
val current = (remainder.toLong() shl 32) or (words[i].toLong() and 0xFFFFFFFFL)
|
||||||
|
quotient[i] = (current / divisor.toLong()).toInt()
|
||||||
|
remainder = (current % divisor.toLong()).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
quotient.size > 1 && quotient.last() == 0 -> {
|
||||||
|
val contracted = quotient.copyOfRange(0, quotient.size - 1)
|
||||||
|
return Pair(BigInt(contracted), remainder)
|
||||||
|
}
|
||||||
|
else -> return Pair(BigInt(quotient), remainder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toByteArray(): ByteArray {
|
||||||
|
val bytes = ByteArray((words.size * 4) - words.last().countLeadingZeroBytes())
|
||||||
|
for (i in bytes.indices) {
|
||||||
|
val (wi, bi) = (bytes.lastIndex - i).divRem(4)
|
||||||
|
bytes[i] = (words[wi] ushr (bi * 8)).toByte()
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val zero: BigInt
|
||||||
|
get() = BigInt(intArrayOf(0))
|
||||||
|
|
||||||
|
fun from(bytes: ByteArray): BigInt {
|
||||||
|
val words = IntArray(bytes.size.divCeil(4))
|
||||||
|
for (i in bytes.indices) {
|
||||||
|
val (wi, bi) = (bytes.lastIndex - i).divRem(4)
|
||||||
|
words[wi] = words[wi] or ((bytes[i].toInt() and 0xFF) shl (bi * 8))
|
||||||
|
}
|
||||||
|
return BigInt(words)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/commonMain/kotlin/com/infendro/encoding/util/Int.kt
Normal file
7
src/commonMain/kotlin/com/infendro/encoding/util/Int.kt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package com.infendro.encoding.util
|
||||||
|
|
||||||
|
fun Int.divRem(divisor: Int): Pair<Int, Int> =
|
||||||
|
Pair(this / divisor, this % divisor)
|
||||||
|
|
||||||
|
fun Int.countLeadingZeroBytes(): Int =
|
||||||
|
countLeadingZeroBits() / 8
|
||||||
@@ -2,14 +2,16 @@ package com.infendro.encoding.util
|
|||||||
|
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
|
|
||||||
internal fun log2(
|
internal fun Int.divCeil(n: Int): Int =
|
||||||
value: Int,
|
(this + n - 1) / n
|
||||||
) = log2(value.toDouble())
|
|
||||||
|
|
||||||
internal fun gcd(
|
internal fun Int.align(n: Int): Int =
|
||||||
a: Int,
|
divCeil(n) * n
|
||||||
b: Int,
|
|
||||||
): Int {
|
internal fun log2(value: Int): Double =
|
||||||
|
log2(value.toDouble())
|
||||||
|
|
||||||
|
internal fun gcd(a: Int, b: Int): Int {
|
||||||
var x = a
|
var x = a
|
||||||
var y = b
|
var y = b
|
||||||
while (y != 0) {
|
while (y != 0) {
|
||||||
@@ -20,9 +22,6 @@ internal fun gcd(
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun lcm(
|
internal fun lcm(a: Int, b: Int): Int {
|
||||||
a: Int,
|
|
||||||
b: Int,
|
|
||||||
): Int {
|
|
||||||
return (a * b) / gcd(a, b)
|
return (a * b) / gcd(a, b)
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/commonTest/kotlin/com/infendro/encoding/Base10.kt
Normal file
36
src/commonTest/kotlin/com/infendro/encoding/Base10.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
|
||||||
|
class `Base10 Test` {
|
||||||
|
val encoding: Encoding
|
||||||
|
get() = Base10
|
||||||
|
|
||||||
|
val original = "Hello World!".encodeToByteArray()
|
||||||
|
val encoded = "22405534230753928650781647905".encodeToByteArray()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `encode empty`() {
|
||||||
|
val actual = encoding.encode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `decode empty`() {
|
||||||
|
val actual = encoding.decode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun encode() {
|
||||||
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,27 +7,30 @@ class `Base16 Test` {
|
|||||||
val encoding: Encoding
|
val encoding: Encoding
|
||||||
get() = Base16
|
get() = Base16
|
||||||
|
|
||||||
val `Hello World!` = ubyteArrayOf(
|
val original = "Hello World!".encodeToByteArray()
|
||||||
0x48U, 0x65U, 0x6cU, 0x6cU,
|
val encoded = "48656C6C6F20576F726C6421".encodeToByteArray()
|
||||||
0x6fU, 0x20U, 0x57U, 0x6fU,
|
|
||||||
0x72U, 0x6cU, 0x64U, 0x21U,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode empty ByteArray`() {
|
fun `encode empty`() {
|
||||||
val expected = ubyteArrayOf()
|
val actual = encoding.encode(byteArrayOf())
|
||||||
val actual = encoding.encode(ubyteArrayOf())
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
assertContentEquals(expected, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode "Hello, World!"`() {
|
fun `decode empty`() {
|
||||||
val expected = ubyteArrayOf(
|
val actual = encoding.decode(byteArrayOf())
|
||||||
0x34U, 0x38U, 0x36U, 0x35U, 0x36U, 0x63U, 0x36U, 0x63U,
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
0x36U, 0x66U, 0x32U, 0x30U, 0x35U, 0x37U, 0x36U, 0x66U,
|
}
|
||||||
0x37U, 0x32U, 0x36U, 0x63U, 0x36U, 0x34U, 0x32U, 0x31U,
|
|
||||||
)
|
@Test
|
||||||
val actual = encoding.encode(`Hello World!`)
|
fun encode() {
|
||||||
assertContentEquals(expected, actual)
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,36 +7,30 @@ class `Base2 Test` {
|
|||||||
val encoding: Encoding
|
val encoding: Encoding
|
||||||
get() = Base2
|
get() = Base2
|
||||||
|
|
||||||
val `Hello World!` = ubyteArrayOf(
|
val original = "Hello World!".encodeToByteArray()
|
||||||
0x48U, 0x65U, 0x6cU, 0x6cU,
|
val encoded = "010010000110010101101100011011000110111100100000010101110110111101110010011011000110010000100001".encodeToByteArray()
|
||||||
0x6fU, 0x20U, 0x57U, 0x6fU,
|
|
||||||
0x72U, 0x6cU, 0x64U, 0x21U,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode empty ByteArray`() {
|
fun `encode empty`() {
|
||||||
val expected = ubyteArrayOf()
|
val actual = encoding.encode(byteArrayOf())
|
||||||
val actual = encoding.encode(ubyteArrayOf())
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
assertContentEquals(expected, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode "Hello, World!"`() {
|
fun `decode empty`() {
|
||||||
val expected = ubyteArrayOf(
|
val actual = encoding.decode(byteArrayOf())
|
||||||
0x30U, 0x31U, 0x30U, 0x30U, 0x31U, 0x30U, 0x30U, 0x30U,
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
0x30U, 0x31U, 0x31U, 0x30U, 0x30U, 0x31U, 0x30U, 0x31U,
|
}
|
||||||
0x30U, 0x31U, 0x31U, 0x30U, 0x31U, 0x31U, 0x30U, 0x30U,
|
|
||||||
0x30U, 0x31U, 0x31U, 0x30U, 0x31U, 0x31U, 0x30U, 0x30U,
|
@Test
|
||||||
0x30U, 0x31U, 0x31U, 0x30U, 0x31U, 0x31U, 0x31U, 0x31U,
|
fun encode() {
|
||||||
0x30U, 0x30U, 0x31U, 0x30U, 0x30U, 0x30U, 0x30U, 0x30U,
|
val actual = encoding.encode(original)
|
||||||
0x30U, 0x31U, 0x30U, 0x31U, 0x30U, 0x31U, 0x31U, 0x31U,
|
assertContentEquals(encoded, actual)
|
||||||
0x30U, 0x31U, 0x31U, 0x30U, 0x31U, 0x31U, 0x31U, 0x31U,
|
}
|
||||||
0x30U, 0x31U, 0x31U, 0x31U, 0x30U, 0x30U, 0x31U, 0x30U,
|
|
||||||
0x30U, 0x31U, 0x31U, 0x30U, 0x31U, 0x31U, 0x30U, 0x30U,
|
@Test
|
||||||
0x30U, 0x31U, 0x31U, 0x30U, 0x30U, 0x31U, 0x30U, 0x30U,
|
fun decode() {
|
||||||
0x30U, 0x30U, 0x31U, 0x30U, 0x30U, 0x30U, 0x30U, 0x31U,
|
val actual = encoding.decode(encoded)
|
||||||
)
|
assertContentEquals(original, actual)
|
||||||
val actual = encoding.encode(`Hello World!`)
|
|
||||||
assertContentEquals(expected, actual)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,27 +7,30 @@ class `Base32 Test` {
|
|||||||
val encoding: Encoding
|
val encoding: Encoding
|
||||||
get() = Base32
|
get() = Base32
|
||||||
|
|
||||||
val `Hello World!` = ubyteArrayOf(
|
val original = "Hello World!".encodeToByteArray()
|
||||||
0x48U, 0x65U, 0x6cU, 0x6cU,
|
val encoded = "JBSWY3DPEBLW64TMMQQQ====".encodeToByteArray()
|
||||||
0x6fU, 0x20U, 0x57U, 0x6fU,
|
|
||||||
0x72U, 0x6cU, 0x64U, 0x21U,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode empty ByteArray`() {
|
fun `encode empty`() {
|
||||||
val expected = ubyteArrayOf()
|
val actual = encoding.encode(byteArrayOf())
|
||||||
val actual = encoding.encode(ubyteArrayOf())
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
assertContentEquals(expected, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode "Hello, World!"`() {
|
fun `decode empty`() {
|
||||||
val expected = ubyteArrayOf(
|
val actual = encoding.decode(byteArrayOf())
|
||||||
0x4aU, 0x42U, 0x53U, 0x57U, 0x59U, 0x33U, 0x44U, 0x50U,
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
0x45U, 0x42U, 0x4cU, 0x57U, 0x36U, 0x34U, 0x54U, 0x4dU,
|
}
|
||||||
0x4dU, 0x51U, 0x51U, 0x51U, 0x3dU, 0x3dU, 0x3dU, 0x3dU,
|
|
||||||
)
|
@Test
|
||||||
val actual = encoding.encode(`Hello World!`)
|
fun encode() {
|
||||||
assertContentEquals(expected, actual)
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/commonTest/kotlin/com/infendro/encoding/Base36.kt
Normal file
36
src/commonTest/kotlin/com/infendro/encoding/Base36.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
|
||||||
|
class `Base36 Test` {
|
||||||
|
val encoding: Encoding
|
||||||
|
get() = Base36
|
||||||
|
|
||||||
|
val original = "Hello World!".encodeToByteArray()
|
||||||
|
val encoded = "2678LX5GVMSV1DRO9B5".encodeToByteArray()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `encode empty`() {
|
||||||
|
val actual = encoding.encode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `decode empty`() {
|
||||||
|
val actual = encoding.decode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun encode() {
|
||||||
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/commonTest/kotlin/com/infendro/encoding/Base45.kt
Normal file
36
src/commonTest/kotlin/com/infendro/encoding/Base45.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
|
||||||
|
class `Base45 Test` {
|
||||||
|
val encoding: Encoding
|
||||||
|
get() = Base45
|
||||||
|
|
||||||
|
val original = "Hello World!".encodeToByteArray()
|
||||||
|
val encoded = "%69 VD82EI2B.KESTC".encodeToByteArray()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `encode empty`() {
|
||||||
|
val actual = encoding.encode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `decode empty`() {
|
||||||
|
val actual = encoding.decode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun encode() {
|
||||||
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/commonTest/kotlin/com/infendro/encoding/Base56.kt
Normal file
36
src/commonTest/kotlin/com/infendro/encoding/Base56.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
|
||||||
|
class `Base56 Test` {
|
||||||
|
val encoding: Encoding
|
||||||
|
get() = Base56
|
||||||
|
|
||||||
|
val original = "Hello World!".encodeToByteArray()
|
||||||
|
val encoded = "4Q9SNpVv4JrdwvjKj".encodeToByteArray()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `encode empty`() {
|
||||||
|
val actual = encoding.encode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `decode empty`() {
|
||||||
|
val actual = encoding.decode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun encode() {
|
||||||
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/commonTest/kotlin/com/infendro/encoding/Base58.kt
Normal file
36
src/commonTest/kotlin/com/infendro/encoding/Base58.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
|
||||||
|
class `Base58 Test` {
|
||||||
|
val encoding: Encoding
|
||||||
|
get() = Base58
|
||||||
|
|
||||||
|
val original = "Hello World!".encodeToByteArray()
|
||||||
|
val encoded = "2NEpo7TZRRrLZSi2U".encodeToByteArray()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `encode empty`() {
|
||||||
|
val actual = encoding.encode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `decode empty`() {
|
||||||
|
val actual = encoding.decode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun encode() {
|
||||||
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/commonTest/kotlin/com/infendro/encoding/Base62.kt
Normal file
36
src/commonTest/kotlin/com/infendro/encoding/Base62.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.infendro.encoding
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
|
||||||
|
class `Base62 Test` {
|
||||||
|
val encoding: Encoding
|
||||||
|
get() = Base62
|
||||||
|
|
||||||
|
val original = "Hello World!".encodeToByteArray()
|
||||||
|
val encoded = "T8dgcjRGkZ3aysdN".encodeToByteArray()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `encode empty`() {
|
||||||
|
val actual = encoding.encode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `decode empty`() {
|
||||||
|
val actual = encoding.decode(byteArrayOf())
|
||||||
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun encode() {
|
||||||
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,26 +7,30 @@ class `Base64 Test` {
|
|||||||
val encoding: Encoding
|
val encoding: Encoding
|
||||||
get() = Base64
|
get() = Base64
|
||||||
|
|
||||||
val `Hello World!` = ubyteArrayOf(
|
val original = "Hello World!".encodeToByteArray()
|
||||||
0x48U, 0x65U, 0x6cU, 0x6cU,
|
val encoded = "SGVsbG8gV29ybGQh".encodeToByteArray()
|
||||||
0x6fU, 0x20U, 0x57U, 0x6fU,
|
|
||||||
0x72U, 0x6cU, 0x64U, 0x21U,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode empty ByteArray`() {
|
fun `encode empty`() {
|
||||||
val expected = ubyteArrayOf()
|
val actual = encoding.encode(byteArrayOf())
|
||||||
val actual = encoding.encode(ubyteArrayOf())
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
assertContentEquals(expected, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode "Hello, World!"`() {
|
fun `decode empty`() {
|
||||||
val expected = ubyteArrayOf(
|
val actual = encoding.decode(byteArrayOf())
|
||||||
0x53U, 0x47U, 0x56U, 0x73U, 0x62U, 0x47U, 0x38U, 0x67U,
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
0x56U, 0x32U, 0x39U, 0x79U, 0x62U, 0x47U, 0x51U, 0x68U,
|
}
|
||||||
)
|
|
||||||
val actual = encoding.encode(`Hello World!`)
|
@Test
|
||||||
assertContentEquals(expected, actual)
|
fun encode() {
|
||||||
|
val actual = encoding.encode(original)
|
||||||
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,28 +7,30 @@ class `Base8 Test` {
|
|||||||
val encoding: Encoding
|
val encoding: Encoding
|
||||||
get() = Base8
|
get() = Base8
|
||||||
|
|
||||||
val `Hello World!` = ubyteArrayOf(
|
val original = "Hello World!".encodeToByteArray()
|
||||||
0x48U, 0x65U, 0x6cU, 0x6cU,
|
val encoded = "22062554330674402566756233062041".encodeToByteArray()
|
||||||
0x6fU, 0x20U, 0x57U, 0x6fU,
|
|
||||||
0x72U, 0x6cU, 0x64U, 0x21U,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode empty ByteArray`() {
|
fun `encode empty`() {
|
||||||
val expected = ubyteArrayOf()
|
val actual = encoding.encode(byteArrayOf())
|
||||||
val actual = encoding.encode(ubyteArrayOf())
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
assertContentEquals(expected, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `encode "Hello, World!"`() {
|
fun `decode empty`() {
|
||||||
val expected = ubyteArrayOf(
|
val actual = encoding.decode(byteArrayOf())
|
||||||
0x32U, 0x32U, 0x30U, 0x36U, 0x32U, 0x35U, 0x35U, 0x34U,
|
assertContentEquals(byteArrayOf(), actual)
|
||||||
0x33U, 0x33U, 0x30U, 0x36U, 0x37U, 0x34U, 0x34U, 0x30U,
|
}
|
||||||
0x32U, 0x35U, 0x36U, 0x36U, 0x37U, 0x35U, 0x36U, 0x32U,
|
|
||||||
0x33U, 0x33U, 0x30U, 0x36U, 0x32U, 0x30U, 0x34U, 0x31U,
|
@Test
|
||||||
)
|
fun encode() {
|
||||||
val actual = encoding.encode(`Hello World!`)
|
val actual = encoding.encode(original)
|
||||||
assertContentEquals(expected, actual)
|
assertContentEquals(encoded, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decode() {
|
||||||
|
val actual = encoding.decode(encoded)
|
||||||
|
assertContentEquals(original, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user