All Downloads are FREE. Search and download functionalities are using the official Maven repository.

upack.Msg.scala Maven / Gradle / Ivy

The newest version!
package upack

import upickle.core.{ArrVisitor, ObjVisitor, LinkedHashMap, Visitor}

import upickle.core.compat._
import scala.collection.mutable

/**
  * In-memory representation of the MessagePack data model
  *
  test - https://msgpack.org/index.html
  *
  * Note that we do not model all the fine details of the MessagePack format
  * in this type; fixed and variable length strings/maps/arrays are all
  * modelled using the same [[Str]]/[[Obj]]/[[Arr]] types, and the various
  * sized integers are all collapsed into [[Int32]]/[[Int64]]/[[UInt64]]. The
  * appropriately sized versions are written out when the message is serialized
  * to bytes.
  */
sealed trait Msg extends Readable with geny.Writable{
  override def httpContentType = Some("application/octet-stream")
  def transform[T](f: Visitor[_, T]) = Msg.transform(this, f)

  /**
    * Returns the `String` value of this [[Msg]], fails if it is not
    * a [[upack.Str]]
    */
  def binary = this match{
    case Binary(value) => value
    case _ => throw Msg.InvalidData(this, "Expected ujson.Str")
  }
  /**
    * Returns the `String` value of this [[Msg]], fails if it is not
    * a [[upack.Str]]
    */
  def str = this match{
    case Str(value) => value
    case _ => throw Msg.InvalidData(this, "Expected ujson.Str")
  }
  /**
    * Returns the key/value map of this [[Msg]], fails if it is not
    * a [[upack.Obj]]
    */
  def obj = this match{
    case Obj(value) => value
    case _ => throw Msg.InvalidData(this, "Expected ujson.Obj")
  }
  /**
    * Returns the elements of this [[Msg]], fails if it is not
    * a [[upack.Arr]]
    */
  def arr = this match{
    case Arr(value) => value
    case _ => throw Msg.InvalidData(this, "Expected ujson.Arr")
  }
  /**
    * Returns the `Double` value of this [[Msg]], fails if it is not
    * a [[upack.Int32]], [[upack.Int64]] or [[upack.UInt64]]
    */
  def int32 = this match{
    case Int32(value) => value
    case Int64(value) => value.toInt
    case UInt64(value) => value.toInt
    case _ => throw Msg.InvalidData(this, "Expected ujson.Num")
  }
  /**
    * Returns the `Double` value of this [[Msg]], fails if it is not
    * a [[upack.Int32]], [[upack.Int64]] or [[upack.UInt64]]
    */
  def int64 = this match{
    case Int32(value) => value.toLong
    case Int64(value) => value
    case UInt64(value) => value
    case _ => throw Msg.InvalidData(this, "Expected ujson.Num")
  }
  /**
    * Returns the `Boolean` value of this [[Msg]], fails if it is not
    * a [[upack.Bool]]
    */
  def bool = this match{
    case Bool(value) => value
    case _ => throw Msg.InvalidData(this, "Expected ujson.Bool")
  }
  /**
    * Returns true if the value of this [[Msg]] is ujson.Null, false otherwise
    */
  def isNull = this match {
    case Null => true
    case _ => false
  }

  def writeBytesTo(out: java.io.OutputStream): Unit = {
    this.transform(new MsgPackWriter(out))
  }
}
case object Null extends Msg
case object True extends Bool{
  def value = true
}
case object False extends Bool{
  def value = false
}
case class Int32(value: Int) extends Msg
case class Int64(value: Long) extends Msg
case class UInt64(value: Long) extends Msg
case class Float32(value: Float) extends Msg
case class Float64(value: Double) extends Msg
case class Str(value: String) extends Msg
case class Binary(value: Array[Byte]) extends Msg
case class Arr(value: mutable.ArrayBuffer[Msg]) extends Msg
object Arr {
  def apply(items: Msg*): Arr = {
    val buf = new mutable.ArrayBuffer[Msg](items.size)
    items.foreach(item => buf += item)
    Arr(buf)
  }
}
case class Obj(value: LinkedHashMap[Msg, Msg]) extends Msg
object Obj{
  def apply(item: (Msg, Msg),
            items: (Msg, Msg)*): Obj = {
    val map = LinkedHashMap[Msg, Msg]()
    map.put(item._1, item._2)
    for (i <- items) map.put(i._1, i._2)
    Obj(map)
  }

  def apply(): Obj = Obj(LinkedHashMap[Msg, Msg]())
}
case class Ext(tag: Byte, data: Array[Byte]) extends Msg

sealed abstract class Bool extends Msg{
  def value: Boolean
}
object Bool{
  def apply(value: Boolean): Bool = if (value) True else False
  def unapply(bool: Bool): Some[Boolean] = Some(bool.value)
}

object Msg extends MsgVisitor[Msg, Msg]{
  /**
    * Thrown when uPickle tries to convert a JSON blob into a given data
    * structure but fails because part the blob is invalid
    *
    * @param data The section of the JSON blob that uPickle tried to convert.
    *             This could be the entire blob, or it could be some subtree.
    * @param msg Human-readable text saying what went wrong
    */
  case class InvalidData(data: Msg, msg: String)
    extends Exception(s"$msg (data: $data)")

  sealed trait Selector{
    def apply(x: Msg): Msg
    def update(x: Msg, y: Msg): Unit
  }
  object Selector{
    implicit class IntSelector(i: Int) extends Selector{
      def apply(x: Msg): Msg = x.arr(i)
      def update(x: Msg, y: Msg) = x.arr(i) = y
    }
    implicit class StringSelector(i: String) extends Selector{
      def apply(x: Msg): Msg = x.obj(Str(i))
      def update(x: Msg, y: Msg) = x.obj(Str(i)) = y
    }
    implicit class MsgSelector(i: Msg) extends Selector{
      def apply(x: Msg): Msg = x.obj(i)
      def update(x: Msg, y: Msg) = x.obj(i) = y
    }
  }
  def transform[T](j: Msg, f: Visitor[_, T]): T = {
    j match{
      case Null => f.visitNull(-1)
      case True => f.visitTrue(-1)
      case False => f.visitFalse(-1)

      case Int32(value) => f.visitInt32(value, -1)
      case Int64(value) => f.visitInt64(value, -1)

      case UInt64(value) => f.visitUInt64(value, -1)

      case Float32(value) => f.visitFloat32(value, -1)
      case Float64(value) => f.visitFloat64(value, -1)

      case Str(value) => f.visitString(value, -1)
      case Binary(value) => f.visitBinary(value, 0, value.length, -1)

      case Arr(items) => visitArrContents(f, items)

      case Obj(items) => visitObjContents(f, items)
      case Ext(tag, data) => f.visitExt(tag, data, 0, data.length, -1)

    }
  }

  private def visitObjContents[T](f: Visitor[_, T], items: LinkedHashMap[Msg, Msg]) = {
    val obj = f.visitObject(items.size, true, -1)
    val kvs = items.iterator
    while(kvs.hasNext){
      val kv = kvs.next()
      val k = kv._1
      val v = kv._2
      val keyVisitor = obj.visitKey(-1)
      obj.visitKeyValue(k.transform(keyVisitor))
      obj.narrow.visitValue(transform(v, obj.subVisitor), -1)
    }
    obj.visitEnd(-1)
  }

  private def visitArrContents[T](f: Visitor[_, T], items: mutable.ArrayBuffer[Msg]) = {
    val arr = f.visitArray(items.length, -1)
    var i = 0
    val itemsLength = items.length
    while (i < itemsLength) {
      arr.narrow.visitValue(transform(items(i), arr.subVisitor), -1)
      i += 1
    }
    arr.visitEnd(-1)
  }

  def visitArray(length: Int, index: Int) = new ArrVisitor[Msg, Msg] {
    val arr = mutable.ArrayBuffer[Msg]()
    def subVisitor = Msg
    def visitValue(v: Msg, index: Int): Unit = arr.append(v)
    def visitEnd(index: Int) = Arr(arr)
  }

  def visitObject(length: Int, jsonableKeys: Boolean, index: Int) = new ObjVisitor[Msg, Msg] {
    val map = LinkedHashMap[Msg, Msg]()
    var lastKey: Msg = null
    def subVisitor = Msg
    def visitValue(v: Msg, index: Int): Unit = map(lastKey) = v
    def visitEnd(index: Int) = Obj(map)
    def visitKey(index: Int) = Msg
    def visitKeyValue(s: Any): Unit = lastKey = s.asInstanceOf[Msg]
  }

  def visitNull(index: Int) = Null

  def visitFalse(index: Int) = False

  def visitTrue(index: Int) = True

  def visitFloat64(d: Double, index: Int) = Float64(d)

  def visitFloat32(d: Float, index: Int) = Float32(d)

  def visitInt32(i: Int, index: Int) = Int32(i)

  def visitInt64(i: Long, index: Int) = Int64(i)

  def visitUInt64(i: Long, index: Int) = UInt64(i)

  def visitString(s: CharSequence, index: Int) = Str(s.toString)

  def visitBinary(bytes: Array[Byte], offset: Int, len: Int, index: Int) =
    Binary(bytes.slice(offset, offset + len))

  def visitExt(tag: Byte, bytes: Array[Byte], offset: Int, len: Int, index: Int) =
    Ext(tag, bytes.slice(offset, offset + len))

  def visitChar(s: Char, index: Int) = Int32(s)

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy