Skip to content

Instantly share code, notes, and snippets.

@danneu
Created July 15, 2017 11:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danneu/c7005aeef1e64a3b0b3881cc2f313366 to your computer and use it in GitHub Desktop.
Save danneu/c7005aeef1e64a3b0b3881cc2f313366 to your computer and use it in GitHub Desktop.
package com.danneu.msgpack
import org.msgpack.core.MessagePack
import org.msgpack.core.MessagePacker
import org.msgpack.core.MessageUnpacker
import org.msgpack.value.ValueType
import java.math.BigInteger
// DEMO: Encode <-> Decode roundtrip
fun main(args: Array<String>) {
val user1 = User("dan", listOf("black", "orange"), 28)
val bytes = MessagePack.newDefaultBufferPacker().use { packer ->
User.encoder(user1)(packer)
packer.toByteArray()
}
println("bytes packed: ${bytes.size}") // bytes packed: 21
val user2 = MessagePack.newDefaultUnpacker(bytes).use { unpacker ->
User.decoder.unpack(unpacker)
}
println("user1: $user1") // user1: User(uname=dan, colors=[black, orange], age=28, something=null)
println("user2: $user2") // user2: User(uname=dan, colors=[black, orange], age=28, something=null)
println("are the equal? ${user1 == user2}") // are the equal? true
}
// IMPL
// Couldn't think of better names
typealias Runner = (MessagePacker) -> MessagePacker
typealias Factory <T> = (T) -> Runner
class Encoder<in T>(val init: T.() -> Runner) {
operator fun invoke(item: T) = item.init()
companion object {
val nil: Runner = { packer -> packer.packNil() }
val str: Factory<String> = { v -> { packer -> packer.packString(v) }}
val bool: Factory<Boolean> = { v -> { packer -> packer.packBoolean(v) }}
val int: Factory<Int> = { v -> { packer -> packer.packInt(v) }}
val long: Factory<Long> = { v -> { packer -> packer.packLong(v) }}
val short: Factory<Short> = { v -> { packer -> packer.packShort(v) }}
val bigint: Factory<BigInteger> = { v -> { packer -> packer.packBigInteger(v) }}
val double: Factory<Double> = { v -> { packer -> packer.packDouble(v) }}
val float: Factory<Float> = { v -> { packer -> packer.packFloat(v) }}
fun listOf(runners: List<Runner>): Runner = { packer ->
packer.packArrayHeader(runners.size)
runners.forEach { it(packer) }
packer
}
fun listOf(vararg runners: Runner): Runner = listOf(runners.toList())
// Higher order
fun <T> nullable(encoder: Factory<T>): Factory<T?> = { v -> { packer -> when (v) {
null -> nil(packer)
else -> encoder(v)(packer)
}}}
}
}
data class User(val uname: String, val colors: List<String>, val age: Int, val something: Boolean? = null) {
companion object {
val decoder: Decoder<User> = Decoder.map(
::User,
Decoder.str,
Decoder.listOf(Decoder.str),
Decoder.int,
Decoder.nullable(Decoder.bool)
)
val encoder = Encoder<User> {
Encoder.listOf(
Encoder.str(uname),
Encoder.listOf(colors.map { Encoder.str(it) }),
Encoder.int(age),
Encoder.nullable(Encoder.bool)(something)
)
}
// IDEAL: val codec = Codec<User> { ... } that somehow fuses/DRYs them?
}
}
class Decoder<out T>(val unpack: (MessageUnpacker) -> T) {
operator fun invoke(unpacker: MessageUnpacker): T = unpack(unpacker)
fun <T2> andThen(f: (T) -> Decoder<T2>) = Decoder { u ->
f(this.unpack(u)).unpack(u)
}
fun <T2> map(f: (T) -> T2) = Decoder { u ->
f(this.unpack(u))
}
companion object {
fun <T> nullable(decoder: Decoder<T>): Decoder<T?> = Decoder { u ->
when (u.nextFormat.valueType) {
ValueType.NIL ->
null
else ->
decoder.unpack(u)
}
}
val bool = Decoder { u ->
println("trying to unpack bool")
u.unpackBoolean().also { println("unpacked bool: $it") }
}
val str = Decoder { u ->
println("trying to unpack string")
u.unpackString().also { println("unpacked string: $it") }
}
val int = Decoder { u ->
println("trying to unpack int")
u.unpackInt().also { println("unpacked int $it") }
}
fun <T> listOf(decoder: Decoder<T>) = Decoder { u ->
val count = u.unpackArrayHeader()
(0..count-1).map {
decoder.unpack(u)
}
}
fun <T> listOf(vararg decoders: Decoder<T>) = listOf(decoders.toList())
fun <T> succeed(value: T) = Decoder {
value
}
fun <V1, T> map(f: (V1) -> T, d1: Decoder<V1>) = Decoder { u -> u.unpackArrayHeader(); f(d1.unpack(u)) }
fun <V1, V2, T> map(f: (V1, V2) -> T, d1: Decoder<V1>, d2: Decoder<V2>) = Decoder { u -> u.unpackArrayHeader(); f(d1.unpack(u), d2.unpack(u)) }
fun <V1, V2, V3, T> map(f: (V1, V2, V3) -> T, d1: Decoder<V1>, d2: Decoder<V2>, d3: Decoder<V3>) = Decoder { u -> u.unpackArrayHeader(); f(d1.unpack(u), d2.unpack(u), d3.unpack(u)) }
fun <V1, V2, V3, V4, T> map(f: (V1, V2, V3, V4) -> T, d1: Decoder<V1>, d2: Decoder<V2>, d3: Decoder<V3>, d4: Decoder<V4>): Decoder<T> = Decoder { u -> u.unpackArrayHeader(); f(d1.unpack(u), d2.unpack(u), d3.unpack(u), d4.unpack(u)) }
fun <V1, V2, V3, V4, V5, T> map(f: (V1, V2, V3, V4, V5) -> T, d1: Decoder<V1>, d2: Decoder<V2>, d3: Decoder<V3>, d4: Decoder<V4>, d5: Decoder<V5>): Decoder<T> = Decoder { u -> u.unpackArrayHeader(); f(d1.unpack(u), d2.unpack(u), d3.unpack(u), d4.unpack(u), d5.unpack(u)) }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment