Skip to content

Instantly share code, notes, and snippets.

@Arneball
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Arneball/9685420 to your computer and use it in GitHub Desktop.
Save Arneball/9685420 to your computer and use it in GitHub Desktop.
"Serialize" an sequence of objects to an sequence of AnyRefs containing the elements of the object
case class PackedArray[T](elems: Array[AnyRef])
object Packer {
def unapply[T](pa: PackedArray[T]): Seq[T] = macro unpack_impl[T]
def unpack_impl[T : c.WeakTypeTag](c: Context)(pa: c.Expr[PackedArray[T]]): c.Expr[Seq[T]] = {
import c.universe._
val tTyp = weakTypeOf[T]
val Some(fields) = tTyp.declarations.collectFirst {
case cons: MethodSymbol if cons.isPrimaryConstructor && !cons.isPublic =>
c.error(c.enclosingPosition, "No public primary constructor found")
Nil
case cons: MethodSymbol if cons.isPrimaryConstructor =>
cons.paramss.map{ syms => syms.map{ sym =>
q"${sym.name}" -> sym.typeSignature
}}
}
val realFields = fields.head
val fieldSize = realFields.size
val must = realFields.zipWithIndex.map{ case ((tree, sym), index) =>
q"$tree = group($index).asInstanceOf[$sym]"
}
val scalaSym = typeOf[Any].typeSymbol.owner
tTyp.baseClasses.collectFirst {
case sym if sym.name.decoded.startsWith("Tuple") && sym.owner == scalaSym =>
c.abort(c.enclosingPosition, "Not needed for tuples!")
}.getOrElse{
c.Expr[Seq[T]]{
q""" $pa.elems.grouped($fieldSize).map{ group =>
new $tTyp(..$must)
}.toList
"""
}
}
}
def pack[T](seq: Seq[T]): PackedArray[T] = macro pack_impl[T]
def pack_impl[T : c.WeakTypeTag](c: Context)(seq: c.Expr[Seq[T]]): c.Expr[PackedArray[T]] = {
import c.universe._
val ttyp = weakTypeOf[T]
val fields = ttyp.declarations.collect {
case sym: MethodSymbol if sym.isCaseAccessor => q"a.${sym.name}.asInstanceOf[AnyRef]"
}
val fieldLen = fields.size
val fieldFun = fields.zipWithIndex.map{ case (select, i) =>
q"array(index * $fieldLen + $i) = $select"
}
val dasBlock = Block(fieldFun.toList:_*)
if (fields.isEmpty) {
c.abort(c.enclosingPosition, "Not a case class!")
} else {
val scalaSym = typeOf[Any].typeSymbol.owner
weakTypeOf[T].baseClasses.collectFirst {
case sym
if sym.name.decoded.startsWith("Tuple") && sym.owner == scalaSym =>
c.abort(c.enclosingPosition, "Not needed for tuples!")
} getOrElse {
c.Expr[PackedArray[T]]{
q"""
val totalSize = $seq.size * $fieldLen
val array = new Array[AnyRef](totalSize)
$seq.zipWithIndex.foreach{ case (a, index) =>
$dasBlock
}
new PackedArray[$ttyp](array)
"""
}
}
}
}
}
case class Person(age: Int, name: String)
val packed: PackedArray[Person] = Packer.pack(List(Person(3, "Kalle"), Person(15, "Bertil")))
val elems: Array[AnyRef] = packed.elems
println{
elems mkString "," // prints 3,Kalle,15,Bertil
}
val seq: Seq[Person] = Packer.unapply(packed)
println(seq) // prints List(Person(3,Kalle), Person(15,Bertil))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment