wvlet.airframe.codec.PrimitiveCodec.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of airframe-codec_sjs1_2.13 Show documentation
Show all versions of airframe-codec_sjs1_2.13 Show documentation
Airframe MessagePack-based codec
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package wvlet.airframe.codec
import wvlet.airframe.json.JSON.JSONValue
import wvlet.airframe.json.Json
import wvlet.airframe.msgpack.spi.Value.ExtensionValue
import wvlet.airframe.msgpack.spi.*
import wvlet.airframe.surface.{Primitive, Surface}
import wvlet.airframe.ulid.{PrefixedULID, ULID}
import java.time.Instant
import java.util.Base64
import scala.util.Try
/**
*/
object PrimitiveCodec {
val primitiveCodec: Map[Surface, MessageCodec[_]] = Map(
Primitive.Int -> IntCodec,
Primitive.Long -> LongCodec,
Primitive.Float -> FloatCodec,
Primitive.Double -> DoubleCodec,
Primitive.Boolean -> BooleanCodec,
Primitive.String -> StringCodec,
Primitive.Byte -> ByteCodec,
Primitive.Short -> ShortCodec,
Primitive.Char -> CharCodec,
Primitive.Unit -> UnitCodec,
Primitive.BigInt -> BigIntCodec,
Primitive.BigInteger -> BigIntegerCodec,
// MessagePack types
Surface.of[Value] -> ValueCodec,
Surface.of[MsgPack] -> RawMsgPackCodec,
// JSON types
Surface.of[JSONValue] -> JSONValueCodec,
Surface.of[Json] -> RawJsonCodec,
Surface.of[Any] -> AnyCodec.default
)
val primitiveArrayCodec = Map(
Surface.of[Array[Int]] -> IntArrayCodec,
Surface.of[Array[Long]] -> LongArrayCodec,
Surface.of[Array[Float]] -> FloatArrayCodec,
Surface.of[Array[Double]] -> DoubleArrayCodec,
Surface.of[Array[Boolean]] -> BooleanArrayCodec,
Surface.of[Array[String]] -> StringArrayCodec,
Surface.of[Array[Byte]] -> ByteArrayCodec,
Surface.of[Array[Short]] -> ShortArrayCodec,
Surface.of[Array[Char]] -> CharArrayCodec,
Surface.of[Array[Any]] -> AnyArrayCodec
)
private implicit class RichBoolean(b: Boolean) {
def toInt: Int = if (b) 1 else 0
def toChar: Char = if (b) 1 else 0
def toByte: Byte = if (b) 1 else 0
def toShort: Short = if (b) 1 else 0
}
trait PrimitiveCodec[A] extends MessageCodec[A] {
def surface: Surface
}
object UnitCodec extends PrimitiveCodec[Unit] {
override def surface: Surface = Primitive.Unit
override def pack(p: Packer, v: Unit): Unit = {
// do not pack anything
}
override def unpack(
u: Unpacker,
v: MessageContext
): Unit = {
// Do not read anything
v.setNull
}
}
object ByteCodec extends PrimitiveCodec[Byte] {
override def surface: Surface = Primitive.Byte
override def pack(p: Packer, v: Byte): Unit = {
p.packByte(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Byte): Unit = {
try {
v.setByte(body)
} catch {
case e: IntegerOverflowException =>
v.setIncompatibleFormatException(this, s"${e.getBigInteger} is too large for a Byte value")
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
val f = u.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.INTEGER =>
read(u.unpackByte)
case ValueType.FLOAT =>
read(u.unpackDouble.toByte)
case ValueType.STRING =>
read {
val s = u.unpackString
Try(s.toByte).getOrElse(s.toDouble.toByte)
}
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toByte)
case _ =>
u.skipValue
v.setNull
}
}
}
object CharCodec extends PrimitiveCodec[Char] {
override def surface: Surface = Primitive.Char
override def pack(p: Packer, v: Char): Unit = {
p.packInt(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Char): Unit = {
try {
v.setChar(body)
} catch {
case e: IntegerOverflowException =>
v.setIncompatibleFormatException(this, s"${e.getBigInteger} is too large for a Char value")
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
val f = u.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.INTEGER =>
read(u.unpackInt.toChar)
case ValueType.STRING =>
read {
val s = u.unpackString
if (s.length == 1) {
s.charAt(0)
} else {
s.toDouble.toChar
}
}
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toChar)
case ValueType.FLOAT =>
read(u.unpackDouble.toChar)
case _ =>
u.skipValue
v.setNull
}
}
}
object ShortCodec extends PrimitiveCodec[Short] {
override def surface: Surface = Primitive.Short
override def pack(p: Packer, v: Short): Unit = {
p.packShort(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Short): Unit = {
try {
v.setShort(body)
} catch {
case e: IntegerOverflowException =>
v.setIncompatibleFormatException(this, s"${e.getBigInteger} is too large for a Short value")
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
val f = u.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.INTEGER =>
read(u.unpackShort)
case ValueType.STRING =>
read {
val s = u.unpackString
Try(s.toShort).getOrElse(s.toDouble.toShort)
}
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toShort)
case ValueType.FLOAT =>
read(u.unpackDouble.toShort)
case _ =>
u.skipValue
v.setNull
}
}
}
object IntCodec extends PrimitiveCodec[Int] {
override def surface: Surface = Primitive.Int
override def pack(p: Packer, v: Int): Unit = {
p.packInt(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Int): Unit = {
try {
v.setInt(body)
} catch {
case e: IntegerOverflowException =>
v.setIncompatibleFormatException(this, s"${e.getBigInteger} is too large for an Int value")
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
val f = u.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.INTEGER =>
read(u.unpackInt)
case ValueType.STRING =>
read {
val s = u.unpackString
Try(s.toInt).getOrElse(s.toDouble.toInt)
}
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toInt)
case ValueType.FLOAT =>
read(u.unpackDouble.toInt)
case _ =>
u.skipValue
v.setNull
}
}
}
object LongCodec extends PrimitiveCodec[Long] {
override def surface: Surface = Primitive.Long
override def pack(p: Packer, v: Long): Unit = {
p.packLong(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Long): Unit = {
try {
v.setLong(body)
} catch {
case e: IntegerOverflowException =>
v.setIncompatibleFormatException(this, s"${e.getBigInteger} is too large for a Long value")
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
val f = u.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.INTEGER =>
read(u.unpackLong)
case ValueType.STRING =>
read {
val s = u.unpackString
Try(s.toLong).getOrElse(s.toDouble.toLong)
}
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toInt)
case ValueType.FLOAT =>
read(u.unpackDouble.toLong)
case _ =>
u.skipValue
v.setNull
}
}
}
object BigIntCodec extends PrimitiveCodec[BigInt] {
override def surface: Surface = Primitive.BigInt
override def pack(p: Packer, v: BigInt): Unit = {
if (v.compareTo(BigInt(Long.MaxValue)) <= 0) {
p.packLong(v.longValue)
} else {
p.packString(v.toString(10))
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => BigInt): Unit = {
try {
v.setObject(body)
} catch {
case e: IntegerOverflowException =>
v.setIncompatibleFormatException(this, s"${e.getBigInteger} is too large for a Long value")
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
val f = u.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.INTEGER =>
read(BigInt(u.unpackLong))
case ValueType.STRING =>
read {
val s = u.unpackString
Try(BigInt(s, 10)).getOrElse(BigInt(s.toDouble.toLong))
}
case ValueType.BOOLEAN =>
read(BigInt(u.unpackBoolean.toInt))
case ValueType.FLOAT =>
read(BigInt(u.unpackDouble.toLong))
case _ =>
u.skipValue
v.setNull
}
}
}
object BigIntegerCodec extends PrimitiveCodec[java.math.BigInteger] {
override def surface: Surface = Primitive.BigInteger
override def pack(p: Packer, v: java.math.BigInteger): Unit = {
if (v.compareTo(java.math.BigInteger.valueOf(Long.MaxValue)) <= 0) {
p.packLong(v.longValue())
} else {
p.packString(v.toString(10))
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => java.math.BigInteger): Unit = {
try {
v.setObject(body)
} catch {
case e: IntegerOverflowException =>
v.setIncompatibleFormatException(this, s"${e.getBigInteger} is too large for a Long value")
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
val f = u.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.INTEGER =>
read(java.math.BigInteger.valueOf(u.unpackLong))
case ValueType.STRING =>
read {
val s = u.unpackString
Try(new java.math.BigInteger(s, 10)).getOrElse(java.math.BigInteger.valueOf(s.toDouble.toLong))
}
case ValueType.BOOLEAN =>
read(java.math.BigInteger.valueOf(u.unpackBoolean.toInt))
case ValueType.FLOAT =>
read(java.math.BigInteger.valueOf(u.unpackDouble.toLong))
case _ =>
u.skipValue
v.setNull
}
}
}
object StringCodec extends PrimitiveCodec[String] {
override def surface: Surface = Primitive.String
override def pack(p: Packer, v: String): Unit = {
if (v == null) {
p.packNil
} else {
p.packString(v)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => String): Unit = {
try {
val s = body
v.setString(s)
} catch {
case e: IntegerOverflowException =>
read(e.getBigInteger.toString())
case e: NumberFormatException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
u.getNextFormat.getValueType match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.STRING =>
read(u.unpackString)
case ValueType.INTEGER =>
read(u.unpackLong.toString)
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toString)
case ValueType.FLOAT =>
read(u.unpackDouble.toString)
case ValueType.MAP =>
read(u.unpackValue.toJson)
case ValueType.ARRAY =>
read(u.unpackValue.toJson)
case ValueType.BINARY =>
read {
val len = u.unpackBinaryHeader
Base64.getEncoder.encodeToString(u.readPayload(len))
}
case _ =>
// Use JSON format for unknown types so that we can read arbitrary types as String value
read(u.unpackValue.toJson)
}
}
}
object BooleanCodec extends PrimitiveCodec[Boolean] {
override def surface: Surface = Primitive.Boolean
override def pack(p: Packer, v: Boolean): Unit = {
p.packBoolean(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Boolean): Unit = {
try {
val b = body
v.setBoolean(b)
} catch {
case e: IntegerOverflowException =>
v.setBoolean(e.getBigInteger.doubleValue() != 0.0)
case e: IllegalArgumentException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
u.getNextFormat.getValueType match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.BOOLEAN =>
read(u.unpackBoolean)
case ValueType.STRING =>
read {
val s = u.unpackString
Try(s.toBoolean).getOrElse(s.toDouble != 0.0)
}
case ValueType.INTEGER =>
read(u.unpackLong != 0L)
case ValueType.FLOAT =>
read(u.unpackDouble != 0.0)
case _ =>
u.skipValue
v.setNull
}
}
}
object FloatCodec extends PrimitiveCodec[Float] {
override def surface: Surface = Primitive.Float
override def pack(p: Packer, v: Float): Unit = {
p.packFloat(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Float): Unit = {
try {
v.setFloat(body)
} catch {
case e: IntegerOverflowException =>
v.setFloat(e.getBigInteger.floatValue())
case e: IllegalArgumentException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
u.getNextFormat.getValueType match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.FLOAT =>
read(u.unpackFloat.toFloat)
case ValueType.INTEGER =>
read(u.unpackLong.toFloat)
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toInt.toFloat)
case ValueType.STRING =>
read(u.unpackString.toFloat)
case _ =>
u.skipValue
v.setNull
}
}
}
object DoubleCodec extends PrimitiveCodec[Double] {
override def surface: Surface = Primitive.Double
override def pack(p: Packer, v: Double): Unit = {
p.packDouble(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
def read(body: => Double): Unit = {
try {
v.setDouble(body)
} catch {
case e: IntegerOverflowException =>
v.setDouble(e.getBigInteger.doubleValue())
case e: IllegalArgumentException =>
v.setIncompatibleFormatException(this, e.getMessage)
}
}
u.getNextFormat.getValueType match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.FLOAT =>
read(u.unpackDouble)
case ValueType.INTEGER =>
read(u.unpackLong.toDouble)
case ValueType.BOOLEAN =>
read(u.unpackBoolean.toInt.toDouble)
case ValueType.STRING =>
read(u.unpackString.toDouble)
case _ =>
u.skipValue
v.setNull
}
}
}
trait PrimitiveArrayCodec { self: MessageCodec[_] =>
/**
* Unpack the input as JSON Array (ValueType.STRING) or ValueType.ARRAY
* @param u
* @param v
* @param unpackRawArray
*/
protected def unpackArray(u: Unpacker, v: MessageContext)(unpackRawArray: => Unit): Unit = {
u.getNextFormat.getValueType match {
case ValueType.STRING =>
// Assume it's JSON input
val jsonArray = u.unpackString
val msgpack = JSONCodec.toMsgPack(jsonArray)
val unpacker = MessagePack.newUnpacker(msgpack)
// Parse again
unpack(unpacker, v)
case ValueType.ARRAY =>
unpackRawArray
case other =>
v.setIncompatibleFormatException(this, s"STRING or ARRAY type ie expected, but ${other} is found")
u.skipValue
}
}
}
object IntArrayCodec extends MessageCodec[Array[Int]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Int]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
IntCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Int]
b.sizeHint(len)
(0 until len).foreach { i =>
IntCodec.unpack(u, v)
if (v.isNull) {
// TODO report error?
b += 0
} else {
val l = v.getInt
b += l.toInt
}
}
v.setObject(b.result())
}
}
}
object ShortArrayCodec extends MessageCodec[Array[Short]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Short]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
ShortCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Short]
b.sizeHint(len)
(0 until len).foreach { i =>
IntCodec.unpack(u, v)
if (v.isNull) {
// TODO report error?
b += 0
} else {
val l = v.getShort
if (l.isValidInt) {
b += l.toShort
} else {
// report error?
b += 0
}
}
}
v.setObject(b.result())
}
}
}
object CharArrayCodec extends MessageCodec[Array[Char]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Char]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
CharCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Char]
b.sizeHint(len)
(0 until len).foreach { i =>
CharCodec.unpack(u, v)
if (v.isNull) {
// TODO report error?
b += 0
} else {
val l = v.getLong
if (l.isValidChar) {
b += l.toChar
} else {
// report error?
b += 0
}
}
}
v.setObject(b.result())
}
}
}
object LongArrayCodec extends MessageCodec[Array[Long]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Long]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
LongCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Long]
b.sizeHint(len)
(0 until len).foreach { i =>
LongCodec.unpack(u, v)
if (v.isNull) {
// TODO report error?
b += 0L
} else {
val l = v.getLong
b += l
}
}
v.setObject(b.result())
}
}
}
object FloatArrayCodec extends MessageCodec[Array[Float]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Float]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
FloatCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Float]
b.sizeHint(len)
(0 until len).foreach { i =>
val d = FloatCodec.unpack(u, v)
if (v.isNull) {
// report error?
b += 0
} else {
// TODO check precision
b += v.getDouble.toFloat
}
}
v.setObject(b.result())
}
}
}
object DoubleArrayCodec extends MessageCodec[Array[Double]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Double]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
DoubleCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Double]
b.sizeHint(len)
(0 until len).foreach { i =>
val d = DoubleCodec.unpack(u, v)
if (v.isNull) {
// report error?
b += 0
} else {
b += v.getDouble
}
}
v.setObject(b.result())
}
}
}
object BooleanArrayCodec extends MessageCodec[Array[Boolean]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Boolean]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
BooleanCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Boolean]
b.sizeHint(len)
(0 until len).foreach { i =>
BooleanCodec.unpack(u, v)
if (v.isNull) {
// report error?
b += false
} else {
b += v.getBoolean
}
}
v.setObject(b.result())
}
}
}
object ByteArrayCodec extends MessageCodec[Array[Byte]] {
override def pack(p: Packer, v: Array[Byte]): Unit = {
p.packBinaryHeader(v.length)
p.addPayload(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
u.getNextValueType match {
case ValueType.BINARY =>
val len = u.unpackBinaryHeader
val b = u.readPayload(len)
v.setObject(b)
case ValueType.STRING =>
val strByteLen = u.unpackRawStringHeader
val strBinary = u.readPayload(strByteLen)
val arr: Array[Byte] =
try {
// Try decoding as base64
Base64.getDecoder.decode(strBinary)
} catch {
case e: IllegalArgumentException =>
// Raw string
strBinary
}
v.setObject(arr)
case _ =>
// Set MessagePack binary
val value = u.unpackValue
v.setObject(value.toMsgpack)
}
}
}
object RawMsgPackCodec extends MessageCodec[MsgPack] {
override def pack(p: Packer, v: Array[Byte]): Unit = {
p.packBinaryHeader(v.length)
p.addPayload(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
u.getNextValueType match {
case ValueType.BINARY =>
val len = u.unpackBinaryHeader
val b = u.readPayload(len)
v.setObject(b)
case _ =>
// Set MessagePack binary
val value = u.unpackValue
v.setObject(value.toMsgpack)
}
}
}
object StringArrayCodec extends MessageCodec[Array[String]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[String]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
StringCodec.pack(p, x)
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[String]
b.sizeHint(len)
(0 until len).foreach { i =>
StringCodec.unpack(u, v)
if (v.isNull) {
b += "" // or report error?
} else {
b += v.getString
}
}
v.setObject(b.result())
}
}
}
object AnyArrayCodec extends MessageCodec[Array[Any]] with PrimitiveArrayCodec {
override def pack(p: Packer, v: Array[Any]): Unit = {
p.packArrayHeader(v.length)
v.foreach { x =>
AnyCodec.default.pack(p, x)
}
}
override def unpack(
u: Unpacker,
v: MessageContext
): Unit = {
unpackArray(u, v) {
val len = u.unpackArrayHeader
val b = Array.newBuilder[Any]
b.sizeHint(len)
(0 until len).foreach { i =>
AnyCodec.default.unpack(u, v)
if (v.isNull) {
b += null // or report error?
} else {
b += v.getLastValue
}
}
v.setObject(b.result())
}
}
}
/**
* MessagePack value codec
*/
object ValueCodec extends MessageCodec[Value] {
override def pack(p: Packer, v: Value): Unit = {
p.packValue(v)
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
v.setObject(u.unpackValue)
}
}
object AnyCodec {
val default: AnyCodec = new AnyCodec()
}
/**
* Codec for Any values. This only supports very basic types to enable packing/unpacking collections like Seq[Any],
* Map[Any, Any] at ease.
*
* Another option to implement AnyCodec is packing pairs of (type, value), but we will not take this approach as this
* will require many bytes to fully encode type names.
*/
class AnyCodec(
codecFactory: MessageCodecFactory = MessageCodecFactory.defaultFactoryForJSON,
knownSurfaces: Seq[Surface] = Seq.empty
) extends MessageCodec[Any]
with AnyCodecCompat {
private val knownSurfaceTable = knownSurfaces.map(s => s.rawType -> s).toMap[Class[_], Surface]
override def pack(p: Packer, v: Any): Unit = {
v match {
case null => p.packNil
// Primitive types
case v: String => StringCodec.pack(p, v)
case v: Boolean => BooleanCodec.pack(p, v)
case v: Int => IntCodec.pack(p, v)
case v: Long => LongCodec.pack(p, v)
case v: Float => FloatCodec.pack(p, v)
case v: Double => DoubleCodec.pack(p, v)
case v: Byte => ByteCodec.pack(p, v)
case v: Short => ShortCodec.pack(p, v)
case v: Char => CharCodec.pack(p, v)
case v: JSONValue => JSONValueCodec.pack(p, v)
case v: Value => ValueCodec.pack(p, v)
case v: Instant => p.packTimestamp(v)
case ps: PackSupport => ps.pack(p)
case v: ULID => ULIDCodec.pack(p, v)
case v: PrefixedULID => PrefixedULIDCodec.pack(p, v)
// Arrays
case v: Array[String] => StringArrayCodec.pack(p, v)
case v: Array[Boolean] => BooleanArrayCodec.pack(p, v)
case v: Array[Int] => IntArrayCodec.pack(p, v)
case v: Array[Long] => LongArrayCodec.pack(p, v)
case v: Array[Float] => FloatArrayCodec.pack(p, v)
case v: Array[Double] => DoubleArrayCodec.pack(p, v)
case v: Array[Byte] => ByteArrayCodec.pack(p, v)
case v: Array[Short] => ShortArrayCodec.pack(p, v)
case v: Array[Char] => CharArrayCodec.pack(p, v)
case v: Array[_] =>
p.packArrayHeader(v.length)
for (x <- v) {
pack(p, x)
}
// Collections
case v: Option[_] =>
if (v.isEmpty) {
p.packNil
} else {
pack(p, v.get)
}
case v: Seq[_] =>
p.packArrayHeader(v.length)
for (x <- v) {
pack(p, x)
}
case m: Map[_, _] =>
p.packMapHeader(m.size)
for ((k, v) <- m) {
pack(p, k)
pack(p, v)
}
case e: Either[_, _] =>
p.packArrayHeader(2)
e match {
case Left(l) =>
pack(p, l)
p.packNil
case Right(r) =>
p.packNil
pack(p, r)
}
case v: Throwable =>
ThrowableCodec.pack(p, v)
case e if isEnum(e) =>
// Scala 3 EnumValue
StringCodec.pack(p, e.toString)
case _ =>
val cl = v.getClass
knownSurfaceTable.get(cl) match {
case Some(surface) =>
val codec = codecFactory.ofSurface(surface).asInstanceOf[MessageCodec[Any]]
codec.pack(p, v)
case None =>
wvlet.airframe.codec.Compat.codecOfClass(cl, codecFactory) match {
case Some(codec) =>
codec.asInstanceOf[MessageCodec[Any]].pack(p, v)
case None =>
// Pack as a string for unknown types
StringCodec.pack(p, v.toString)
}
}
}
}
override def unpack(u: Unpacker, v: MessageContext): Unit = {
u.getNextValueType match {
case ValueType.NIL =>
u.unpackNil
v.setNull
case ValueType.BOOLEAN =>
v.setBoolean(u.unpackBoolean)
case ValueType.INTEGER =>
v.setLong(u.unpackLong)
case ValueType.FLOAT =>
v.setDouble(u.unpackDouble)
case ValueType.STRING =>
v.setString(u.unpackString)
case ValueType.BINARY =>
val len = u.unpackBinaryHeader
v.setObject(u.readPayload(len))
case ValueType.ARRAY =>
val len = u.unpackArrayHeader
val b = Seq.newBuilder[Any]
b.sizeHint(len)
(0 until len).foreach { i =>
unpack(u, v)
if (v.isNull) {
b += null // or report error?
} else {
b += v.getLastValue
}
}
v.setObject(b.result())
case ValueType.MAP =>
val len = u.unpackMapHeader
val b = Map.newBuilder[Any, Any]
b.sizeHint(len)
for (i <- 0 until len) {
unpack(u, v)
val key = v.getLastValue
unpack(u, v)
val value = v.getLastValue
b += (key -> value)
}
v.setObject(b.result())
case ValueType.EXTENSION =>
val ext = u.unpackExtTypeHeader
if (ext.extType == -1) {
v.setObject(u.unpackTimestamp(ext))
} else {
val extBody = u.readPayload(ext.byteLength)
v.setObject(ExtensionValue(ext.extType, extBody))
}
}
}
}
}