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

de.aktey.akka.visualmailbox.packing.Packing.scala Maven / Gradle / Ivy

The newest version!
package de.aktey.akka.visualmailbox.packing

import java.nio.charset.Charset

import de.aktey.akka.visualmailbox.{MetricEnvelope, VisualMailboxMetric}

import scala.annotation.tailrec
import scala.collection.generic.CanBuildFrom
import scala.collection.immutable._
import scala.collection.mutable
import scala.util.{Failure, Success, Try}


trait Packer[-A] {
  def pack(a: A): Array[Byte]
}

trait Unpacker[A] {
  def unpack(b: Array[Byte]): Try[(A, Array[Byte])]
}

object Packing {
  def pack[A: Packer](a: A): Array[Byte] = implicitly[Packer[A]].pack(a)

  def unpack[A: Unpacker](b: Array[Byte]): Try[A] = implicitly[Unpacker[A]].unpack(b).map(_._1)

  def iunpack[A: Unpacker](b: Array[Byte]): Try[(A, Array[Byte])] = implicitly[Unpacker[A]].unpack(b)
}

trait Packers {
  def charSet: Charset

  implicit object IntPacker extends Packer[Int] {
    override def pack(a: Int): Array[Byte] = {
      Array((a >>> 24).toByte, (a >>> 16).toByte, (a >>> 8).toByte, a.toByte)
    }
  }

  implicit object StringPacker extends Packer[String] {
    override def pack(a: String): Array[Byte] = {
      val packed = a.getBytes(charSet)
      Packing.pack(packed.size) ++ packed
    }
  }

  implicit object EnvelopPacker extends Packer[MetricEnvelope] {
    override def pack(a: MetricEnvelope): Array[Byte] = {
      Packing.pack(a.version) ++ a.payload
    }
  }

  implicit object VisualMailboxMetricPacker extends Packer[VisualMailboxMetric] {
    override def pack(a: VisualMailboxMetric): Array[Byte] =
      Packing.pack(a.receiver) ++
        Packing.pack(a.sender) ++
        Packing.pack(a.receiverMailBoxSize.toString) ++
        Packing.pack(a.meassureTimeMillies.toString)
  }

  implicit def traversablePacker[A, T](implicit ev: T <:< Traversable[A], apacker: Packer[A]): Packer[T] = new Packer[T] {
    override def pack(t: T): Array[Byte] =
      t.foldLeft(Packing.pack(t.size)) {
        (bytes, elem) => bytes ++ Packing.pack(elem)
      }
  }

  implicit def mapPacker[A, B](implicit tpacker: Packer[(A, B)]): Packer[Map[A, B]] = new Packer[Map[A, B]] {
    override def pack(t: Map[A, B]): Array[Byte] =
      t.foldLeft(Packing.pack(t.size)) {
        (bytes, elem) => bytes ++ tpacker.pack(elem)
      }
  }

  implicit def tuple2Packer[A, B](implicit apacker: Packer[A], bpacker: Packer[B]): Packer[(A, B)] = new Packer[(A, B)] {
    override def pack(t: (A, B)): Array[Byte] = apacker.pack(t._1) ++ bpacker.pack(t._2)
  }
}

trait Unpackers {
  def charSet: Charset

  implicit object IntUnpacker extends Unpacker[Int] {
    override def unpack(bytes: Array[Byte]): Try[(Int, Array[Byte])] = Try {
      val intBytes = bytes.take(4)
      val i = (intBytes(0) << 24) | (intBytes(1) << 24 >>> 8) | (intBytes(2) << 24 >>> 16) | (intBytes(3) << 24 >>> 24)
      (i, bytes.drop(4))
    }
  }

  implicit object StringUnpacker extends Unpacker[String] {
    override def unpack(bytes: Array[Byte]): Try[(String, Array[Byte])] =
      Packing.iunpack[Int](bytes).flatMap {
        case (size, rest) => Try {
          val strBytes = rest.take(size)
          (new String(strBytes, charSet), rest.drop(size))
        }
      }
  }

  implicit object EnvelopeUnpacker extends Unpacker[MetricEnvelope] {
    override def unpack(bytes: Array[Byte]): Try[(MetricEnvelope, Array[Byte])] =
      Packing.iunpack[Int](bytes).map {
        case (version, rest) => (MetricEnvelope(version, rest), Array.empty[Byte])
      }
  }

  implicit def traversableUnpacker[A, T](implicit ev: T <:< Traversable[A], bf: CanBuildFrom[List[A], A, T], aunpacker: Unpacker[A]) = new Unpacker[T] {
    override def unpack(bytes: Array[Byte]): Try[(T, Array[Byte])] =
      Packing.iunpack[Int](bytes).flatMap {
        case (size, rest) => unpackRec(bf(), size, rest)
      }

    @tailrec
    private def unpackRec(b: mutable.Builder[A, T], n: Int, bytes: Array[Byte]): Try[(T, Array[Byte])] = {
      n match {
        case 0 =>
          Success((b.result(), bytes))
        case _ =>
          Packing.iunpack[A](bytes) match {
            case Failure(f) => Failure(f)
            case Success((elem, rest)) => unpackRec(b += elem, n - 1, rest)
          }
      }
    }
  }

  implicit object VisualMailboxMetricUnpacker extends Unpacker[VisualMailboxMetric] {
    override def unpack(bytes: Array[Byte]): Try[(VisualMailboxMetric, Array[Byte])] = for {
      (receiver, r1) <- Packing.iunpack[String](bytes)
      (sender, r2) <- Packing.iunpack[String](r1)
      (receiverMailBoxSize, r3) <- Packing.iunpack[String](r2)
      (meassureTimeMillies, r) <- Packing.iunpack[String](r3)
    } yield (VisualMailboxMetric(sender, receiver, receiverMailBoxSize.toInt, meassureTimeMillies.toLong), r)
  }

  implicit def mapUnpacker[A, B](implicit ev: Unpacker[Traversable[(A, B)]], tunpacker: Unpacker[(A, B)]) = new Unpacker[Map[A, B]] {
    override def unpack(bytes: Array[Byte]): Try[(Map[A, B], Array[Byte])] =
      for ((t, rest) <- Packing.iunpack[Traversable[(A, B)]](bytes)) yield (t.toMap, rest)
  }

  implicit def tuple2Unpacker[A, B](implicit aunpacker: Unpacker[A], bunpacker: Unpacker[B]): Unpacker[(A, B)] = new Unpacker[(A, B)] {
    override def unpack(bytes: Array[Byte]): Try[((A, B), Array[Byte])] = for {
      (a, r1) <- Packing.iunpack[A](bytes)
      (b, r2) <- Packing.iunpack[B](r1)
    } yield ((a, b), r2)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy