Skip to content

Instantly share code, notes, and snippets.

@namiazad
Last active February 9, 2018 06:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save namiazad/460f2a6d10afc97203d8 to your computer and use it in GitHub Desktop.
Save namiazad/460f2a6d10afc97203d8 to your computer and use it in GitHub Desktop.
A Scala DSL to convert data to a byte sequence representation
import java.io.{ByteArrayOutputStream, DataOutputStream}
import shapeless._
case class Container(dos: DataOutputStream, baos: ByteArrayOutputStream) {
def ~[A: ByteSequenceRepr](arg: A) = implicitly[ByteSequenceRepr[A]].toByteSequence(this, arg)
def ~| : Array[Byte] = {
dos.close()
baos.toByteArray
}
}
object Container {
def apply() = {
val baos = new ByteArrayOutputStream()
val dos = new DataOutputStream(baos)
new Container(dos, baos)
}
}
trait ByteSequenceRepr[A] {
def writer: (Container, A) => Unit
def toByteSequence(arg: A): Container = {
val baos = new ByteArrayOutputStream()
val dos = new DataOutputStream(baos)
toByteSequence(Container(dos, baos), arg)
}
def toByteSequence(container: Container, arg: A): Container = {
writer(container, arg)
container
}
}
trait LowPriorityDefaultByteSequenceReprImplicits {
implicit val ByteToByteSequence: ByteSequenceRepr[Byte] = new ByteSequenceRepr[Byte] {
val writer: (Container, Byte) => Unit = _.dos.writeByte(_)
}
implicit val intToByteSequence: ByteSequenceRepr[Int] = new ByteSequenceRepr[Int] {
val writer: (Container, Int) => Unit = _.dos.writeInt(_)
}
implicit val longToByteSequence: ByteSequenceRepr[Long] = new ByteSequenceRepr[Long] {
val writer: (Container, Long) => Unit = _.dos.writeLong(_)
}
implicit val floatToByteSequence: ByteSequenceRepr[Float] = new ByteSequenceRepr[Float] {
val writer: (Container, Float) => Unit = _.dos.writeFloat(_)
}
implicit val doubleToByteSequence: ByteSequenceRepr[Double] = new ByteSequenceRepr[Double] {
val writer: (Container, Double) => Unit = _.dos.writeDouble(_)
}
implicit val stringToByteSequence: ByteSequenceRepr[String] = new ByteSequenceRepr[String] {
val writer: (Container, String) => Unit = _.dos.writeUTF(_)
}
implicit val booleanToByteSequence: ByteSequenceRepr[Boolean] = new ByteSequenceRepr[Boolean] {
val writer: (Container, Boolean) => Unit = _.dos.writeBoolean(_)
}
implicit def optionToByteSequence[A: ByteSequenceRepr]: ByteSequenceRepr[Option[A]] =
new ByteSequenceRepr[Option[A]] {
def writer: (Container, Option[A]) => Unit = { (container, opt) =>
opt.foreach(value => implicitly[ByteSequenceRepr[A]].toByteSequence(container, value))
}
}
}
object ByteSequenceRepr extends LowPriorityDefaultByteSequenceReprImplicits
object Implicits {
//implicit conversion from Container to byte array with which you do not need to explicitly call ~| to convert
//Container instance to byte array.
implicit def containerToByteArray(container: Container): Array[Byte] = container.~|
implicit class WithTilde[A](val left: A) extends AnyVal {
def ~[B](right: B)(implicit seqA: ByteSequenceRepr[A],
seqB: ByteSequenceRepr[B]): Container = {
val container = seqA.toByteSequence(left)
seqB.toByteSequence(container, right)
}
def ~|(implicit seqA: ByteSequenceRepr[A]): Array[Byte] = seqA.toByteSequence(left)
}
implicit val HNilByteSequenceRepr: ByteSequenceRepr[HNil] = new ByteSequenceRepr[HNil] {
def writer: (Container, HNil) => Unit = (_, _) => ()
}
implicit def HConsByteSequenceRepr[H, T <: HList](implicit bsrH: Lazy[ByteSequenceRepr[H]],
bsrT: Lazy[ByteSequenceRepr[T]]): ByteSequenceRepr[H :: T] = {
new ByteSequenceRepr[H :: T] {
def writer: (Container, H :: T) => Unit = { (container, cons) =>
cons match {
case (h :: HNil) => bsrH.value.toByteSequence(container, h)
case (h :: t) =>
bsrH.value.toByteSequence(container, h)
bsrT.value.toByteSequence(container, t)
}
}
}
}
implicit def GenericByteSequenceRepr[T, R](implicit gen: Generic.Aux[T, R],
bsr: ByteSequenceRepr[R]): ByteSequenceRepr[T] = {
new ByteSequenceRepr[T] {
def writer: (Container, T) => Unit = { (container, cons) =>
bsr.value.toByteSequence(container, gen.to(cons))
}
}
}
}
import Implicits._
object SampleShapeless extends App {
case class Orange(hasVitaminC: Boolean, color: String, size: Int)
case class Apple(age: Int)
val seqRaw: Array[Byte] = true ~ "orange" ~ 10 ~ 20
val seqRich: Array[Byte] = Orange(hasVitaminC = true, "orange", 10) ~ Apple(20)
println(s"Equals: ${util.Arrays.equals(seqRaw, seqRich)}")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment