Created
July 15, 2017 11:05
-
-
Save danneu/c7005aeef1e64a3b0b3881cc2f313366 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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