Last active
February 9, 2018 06:59
-
-
Save namiazad/460f2a6d10afc97203d8 to your computer and use it in GitHub Desktop.
A Scala DSL to convert data to a byte sequence representation
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
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