Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
language: scala
dist: trusty
scala:
- 2.11.11
- 2.12.8
- 2.11.12
- 2.12.11
jdk:
- oraclejdk8
- openjdk11
12 changes: 8 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@ description := "Scala Macro Based Serialization"

scalaVersion := "2.12.11"

crossScalaVersions := Seq("2.11.11", "2.12.11")
crossScalaVersions := Seq("2.11.12", "2.12.11")

// Needed for the JavaBean tests to work
compileOrder := CompileOrder.JavaThenScala

// NOTE: For -Xelide-below: ALL == Enabled Assertions, OFF == Disabled Assertions
scalacOptions := Seq(
"-encoding", "UTF-8",
"-unchecked",
"-deprecation",
"-language:implicitConversions,experimental.macros",
"-feature",
"-Xlint",
"-Ywarn-unused-import",
"-Xelide-below", "OFF"
"-Xelide-below", "OFF", // ALL == Enabled Assertions, OFF == Disabled Assertions
// Warnings become Errors
"-Xfatal-warnings",
) ++ (if (scalaVersion.value.startsWith("2.12")) Seq(
// Scala 2.12 specific compiler flags
"-opt:l:inline",
"-opt-inline-from:<sources>"
"-opt-inline-from:<sources>",
// Remove "params" since we often have method signatures that intentionally have the parameters, but may not be used in every implementation, also omit "patvars" since it isn't part of the default xlint:unused and isn't super helpful
"-Ywarn-unused:imports,privates,locals",
) else Nil)

// Need to make sure any Java sources are compiled to 1.8 classfile format
Expand Down
8 changes: 4 additions & 4 deletions src/main/scala/fm/serializer/FMByteArrayOutputStream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1012,10 +1012,10 @@ final class FMByteArrayOutputStream(
checkOffsetAndLength(msg, arr, arr.length, off, len)
}

@elidable(elidable.ASSERTION)
private def checkOffsetAndLength(msg: => String, str: CharSequence, off: Int, len: Int): Unit = {
checkOffsetAndLength(msg, str, str.length, off, len)
}
// @elidable(elidable.ASSERTION)
// private def checkOffsetAndLength(msg: => String, str: CharSequence, off: Int, len: Int): Unit = {
// checkOffsetAndLength(msg, str, str.length, off, len)
// }

@elidable(elidable.ASSERTION)
private def checkOffsetAndLength(msg: => String, obj: AnyRef, actualLength: Int, off: Int, len: Int): Unit = {
Expand Down
223 changes: 114 additions & 109 deletions src/main/scala/fm/serializer/MacroHelpers.scala

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions src/main/scala/fm/serializer/Macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,52 +25,52 @@ object Macros {
*/
private val isDebug: Boolean = System.getenv("FM_SERIALIZER_DEBUG") == "true"

def makeSimpleObjectSerializer[T: c.WeakTypeTag](c: Context)(): c.Expr[SimpleObjectSerializer[T]] = wrap(c, s"makeSimpleObjectSerializer[${c.weakTypeOf[T]}]") {
def makeSimpleObjectSerializer[T: c.WeakTypeTag](c: blackbox.Context)(): c.Expr[SimpleObjectSerializer[T]] = wrap(c, s"makeSimpleObjectSerializer[${c.weakTypeOf[T]}]") {

c.universe.reify { SimpleObjectSerializer[T]()(makeObjectSerializer[T](c).splice, makeObjectDeserializer[T](c).splice) }
}

def makeObjectSerializerFromFields[T: c.WeakTypeTag](c: Context)(field: c.Expr[Field], fields: c.Expr[Field]*): c.Expr[ObjectSerializer[T]] = wrap(c, s"makeObjectSerializer[${c.weakTypeOf[T]}]") {
def makeObjectSerializerFromFields[T: c.WeakTypeTag](c: blackbox.Context)(field: c.Expr[Field], fields: c.Expr[Field]*): c.Expr[ObjectSerializer[T]] = wrap(c, s"makeObjectSerializer[${c.weakTypeOf[T]}]") {
object helpers extends MacroHelpers(isDebug){ val ctx: c.type = c }
helpers.makeObjectSerializer((Vector(field)++fields).map{ helpers.makeFieldImpl })
}

def makeObjectDeserializerFromFields[T: c.WeakTypeTag](c: Context)(field: c.Expr[Field], fields: c.Expr[Field]*): c.Expr[ObjectDeserializer[T]] = wrap(c, s"makeObjectDeserializer[${c.weakTypeOf[T]}]") {
def makeObjectDeserializerFromFields[T: c.WeakTypeTag](c: blackbox.Context)(field: c.Expr[Field], fields: c.Expr[Field]*): c.Expr[ObjectDeserializer[T]] = wrap(c, s"makeObjectDeserializer[${c.weakTypeOf[T]}]") {
object helpers extends MacroHelpers(isDebug){ val ctx: c.type = c }
helpers.makeObjectDeserializer((Vector(field)++fields).map{ helpers.makeFieldImpl })
}

def makeObjectSerializerForInterface[IFACE: c.WeakTypeTag, CONCRETE: c.WeakTypeTag](c: Context)(): c.Expr[ObjectSerializer[IFACE]] = wrap(c, s"makeObjectSerializerForInterface[${c.weakTypeOf[IFACE]},${c.weakTypeOf[CONCRETE]}]") {
def makeObjectSerializerForInterface[IFACE: c.WeakTypeTag, CONCRETE: c.WeakTypeTag](c: blackbox.Context)(): c.Expr[ObjectSerializer[IFACE]] = wrap(c, s"makeObjectSerializerForInterface[${c.weakTypeOf[IFACE]},${c.weakTypeOf[CONCRETE]}]") {
object helpers extends MacroHelpers(isDebug){ val ctx: c.type = c }
import helpers._
val ifaceTpe: c.Type = c.weakTypeOf[IFACE]
val concreteTpe: c.Type = c.weakTypeOf[CONCRETE]
tryMakeObjectSerializerForInterface[IFACE, CONCRETE] getOrElse { c.abort(c.enclosingPosition, s"Couldn't make ObjectSerializer for interface $ifaceTpe from concrete type $concreteTpe") }
}

def makeObjectSerializer[T: c.WeakTypeTag](c: Context)(): c.Expr[ObjectSerializer[T]] = wrap(c, s"makeObjectSerializer[${c.weakTypeOf[T]}]") {
def makeObjectSerializer[T: c.WeakTypeTag](c: blackbox.Context)(): c.Expr[ObjectSerializer[T]] = wrap(c, s"makeObjectSerializer[${c.weakTypeOf[T]}]") {
object helpers extends MacroHelpers(isDebug){ val ctx: c.type = c }
import helpers._
val tpe: c.Type = c.weakTypeOf[T]
tryMakeObjectSerializer[T] getOrElse { c.abort(c.enclosingPosition, s"Couldn't make ObjectSerializer for $tpe") }
}

def makeObjectDeserializer[T: c.WeakTypeTag](c: Context)(): c.Expr[ObjectDeserializer[T]] = wrap(c, s"makeObjectDeserializer[${c.weakTypeOf[T]}]") {
def makeObjectDeserializer[T: c.WeakTypeTag](c: blackbox.Context)(): c.Expr[ObjectDeserializer[T]] = wrap(c, s"makeObjectDeserializer[${c.weakTypeOf[T]}]") {
object helpers extends MacroHelpers(isDebug){ val ctx: c.type = c }
import helpers._
val tpe: c.Type = c.weakTypeOf[T]
tryMakeObjectDeserializer[T] getOrElse { c.abort(c.enclosingPosition, s"Couldn't make ObjectDeserializer for $tpe") }
}

def makeSerializerNoImplicits[T: c.WeakTypeTag](c: Context): c.Expr[Serializer[T]] = wrap(c, s"makeSerializerNoImplicits[${c.weakTypeOf[T]}]") {
def makeSerializerNoImplicits[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Serializer[T]] = wrap(c, s"makeSerializerNoImplicits[${c.weakTypeOf[T]}]") {
makeSerializer[T](allowImplicits = false)(c)
}

def makeSerializerAllowImplicits[T: c.WeakTypeTag](c: Context): c.Expr[Serializer[T]] = wrap(c, s"makeSerializerAllowImplicits[${c.weakTypeOf[T]}]") {
def makeSerializerAllowImplicits[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Serializer[T]] = wrap(c, s"makeSerializerAllowImplicits[${c.weakTypeOf[T]}]") {
makeSerializer[T](allowImplicits = true)(c)
}

def makeSerializer[T: c.WeakTypeTag](allowImplicits: Boolean)(c: Context): c.Expr[Serializer[T]] = wrap(c, s"makeSerializer[${c.weakTypeOf[T]}]") {
def makeSerializer[T: c.WeakTypeTag](allowImplicits: Boolean)(c: blackbox.Context): c.Expr[Serializer[T]] = wrap(c, s"makeSerializer[${c.weakTypeOf[T]}]") {

object helpers extends MacroHelpers(isDebug){ val ctx: c.type = c }
import helpers._
Expand All @@ -85,7 +85,7 @@ object Macros {
nonMacroImplicit orElse findCommonType[T] orElse findOptionSerializer[T] orElse findCollectionSerializer[T] orElse findAnyValSerializer[T] orElse tryMakeObjectSerializer[T] getOrElse { c.abort(c.enclosingPosition, s"Couldn't find Serializer for $tpe") }
}

def makeDeserializer[T: c.WeakTypeTag](c: Context): c.Expr[Deserializer[T]] = wrap(c, s"makeDeserializer[${c.weakTypeOf[T]}]") {
def makeDeserializer[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Deserializer[T]] = wrap(c, s"makeDeserializer[${c.weakTypeOf[T]}]") {
object helpers extends MacroHelpers(isDebug){ val ctx: c.type = c }
import helpers._
import c.universe._
Expand All @@ -99,7 +99,7 @@ object Macros {
nonMacroImplicit orElse findCommonType[T] orElse findOptionDeserializer[T] orElse findCollectionDeserializer[T] orElse findAnyValDeserializer[T] orElse tryMakeObjectDeserializer[T] getOrElse { c.abort(c.enclosingPosition, s"Couldn't find Deserializer for $tpe") }
}

private def wrap[T](c: Context, msg: String)(f: => T): T = {
private def wrap[T](c: blackbox.Context, msg: String)(f: => T): T = {
try {
val res: T = f
if (isDebug) c.info(c.enclosingPosition, msg+" => "+res, true)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/fm/serializer/OptionDeserializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import fm.common.Implicits._
/**
* For deserializing Option types. Note: this does NOT allow Some(null)
*/
final case class OptionDeserializer[A](implicit deser: Deserializer[A]) extends Deserializer[Option[A]] {
final case class OptionDeserializer[A]()(implicit deser: Deserializer[A]) extends Deserializer[Option[A]] {
def defaultValue: Option[A] = None
final def deserializeRaw(input: RawInput): Option[A] = Option(deser.deserializeRaw(input))

Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/fm/serializer/OptionSerializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package fm.serializer

final case class OptionSerializer[A](implicit ser: Serializer[A]) extends Serializer[Option[A]] {
final case class OptionSerializer[A]()(implicit ser: Serializer[A]) extends Serializer[Option[A]] {

def serializeRaw(output: RawOutput, v: Option[A]): Unit = if (v.isDefined) ser.serializeRaw(output, v.get)
def serializeNested(output: NestedOutput, v: Option[A]): Unit = if (v.isDefined) ser.serializeNested(output, v.get)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/fm/serializer/SimpleObjectSerializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ object SimpleObjectSerializer {
def make[T](): SimpleObjectSerializer[T] = macro Macros.makeSimpleObjectSerializer[T]
}

final case class SimpleObjectSerializer[T](implicit ser: ObjectSerializer[T], deser: ObjectDeserializer[T]) extends ObjectSerializer[T] with ObjectDeserializer[T] with SimpleSerializer[T] {
final case class SimpleObjectSerializer[T]()(implicit ser: ObjectSerializer[T], deser: ObjectDeserializer[T]) extends ObjectSerializer[T] with ObjectDeserializer[T] with SimpleSerializer[T] {
final def defaultValue: T = deser.defaultValue
final def deserializeRaw(input: RawInput): T = deser.deserializeRaw(input)
final def deserializeNested(input: NestedInput): T = deser.deserializeNested(input)
Expand Down
62 changes: 31 additions & 31 deletions src/main/scala/fm/serializer/protobuf/ProtobufOutput.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ final class ProtobufOutput() extends Output {
def allowStringMap: Boolean = false

private[this] val MaxVarint32Bytes: Int = 5
private[this] val MaxVarint64Bytes: Int = 10
private[this] val EncodeMessagesAsGroups: Boolean = true
//private[this] val MaxVarint64Bytes: Int = 10
//private[this] val EncodeMessagesAsGroups: Boolean = true

private[this] val os: FMByteArrayOutputStream = new FMByteArrayOutputStream()

Expand Down Expand Up @@ -443,7 +443,7 @@ final class ProtobufOutput() extends Output {

// Note: os.lengthPrefixed is marked as @inline to eliminate these closures
os.lengthPrefixed(tagSize + MaxVarint32Bytes){ f(this, obj) }{ length: Int =>
val lengthSize: Int = computeRawVarint32Size(length)
//val lengthSize: Int = computeRawVarint32Size(length)

val array: Array[Byte] = os.array
var offset: Int = os.offset
Expand Down Expand Up @@ -498,40 +498,40 @@ final class ProtobufOutput() extends Output {
}

/** Write a big-endian 32-bit integer. */
private def writeRawBigEndian32(value: Int): Unit = {
os.ensureAvailable(4)
os.offset = writeRawBigEndian32(value, os.array, os.offset)
}
// private def writeRawBigEndian32(value: Int): Unit = {
// os.ensureAvailable(4)
// os.offset = writeRawBigEndian32(value, os.array, os.offset)
// }

/** Write a big-endian 32-bit integer. */
private def writeRawBigEndian32(value: Int, array: Array[Byte], offset: Int): Int = {
array(offset) = ((value >>> 24) & 0xFF).toByte
array(offset+1) = ((value >>> 16) & 0xFF).toByte
array(offset+2) = ((value >>> 8) & 0xFF).toByte
array(offset+3) = ((value ) & 0xFF).toByte
offset + 4
}
// private def writeRawBigEndian32(value: Int, array: Array[Byte], offset: Int): Int = {
// array(offset) = ((value >>> 24) & 0xFF).toByte
// array(offset+1) = ((value >>> 16) & 0xFF).toByte
// array(offset+2) = ((value >>> 8) & 0xFF).toByte
// array(offset+3) = ((value ) & 0xFF).toByte
//
// offset + 4
// }

/** Write a big-endian 64-bit integer. */
private def writeRawBigEndian64(value: Long): Unit = {
os.ensureAvailable(8)
os.offset = writeRawBigEndian64(value, os.array, os.offset)
}
// private def writeRawBigEndian64(value: Long): Unit = {
// os.ensureAvailable(8)
// os.offset = writeRawBigEndian64(value, os.array, os.offset)
// }

/** Write a big-endian 64-bit integer. */
private def writeRawBigEndian64(value: Long, array: Array[Byte], offset: Int): Int = {
array(offset) = (value >>> 56).toByte
array(offset+1) = (value >>> 48).toByte
array(offset+2) = (value >>> 40).toByte
array(offset+3) = (value >>> 32).toByte
array(offset+4) = (value >>> 24).toByte
array(offset+5) = (value >>> 16).toByte
array(offset+6) = (value >>> 8).toByte
array(offset+7) = (value ).toByte
offset + 8
}
// private def writeRawBigEndian64(value: Long, array: Array[Byte], offset: Int): Int = {
// array(offset) = (value >>> 56).toByte
// array(offset+1) = (value >>> 48).toByte
// array(offset+2) = (value >>> 40).toByte
// array(offset+3) = (value >>> 32).toByte
// array(offset+4) = (value >>> 24).toByte
// array(offset+5) = (value >>> 16).toByte
// array(offset+6) = (value >>> 8).toByte
// array(offset+7) = (value ).toByte
//
// offset + 8
// }

private def writeRawVarint32(value: Int): Unit = {
os.ensureAvailable(computeRawVarint32Size(value))
Expand Down
25 changes: 22 additions & 3 deletions src/test/java/fm/serializer/FooJavaBeanContainer.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package fm.serializer;

import java.beans.Transient;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlTransient;

public class FooJavaBeanContainer {
protected List<FooJavaBean> children;
Expand All @@ -15,4 +12,26 @@ public List<FooJavaBean> getChildren() {
public void setChildren(List<FooJavaBean> value) {
this.children = value;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}

if (!FooJavaBeanContainer.class.isAssignableFrom(obj.getClass())) {
return false;
}

final FooJavaBeanContainer other = (FooJavaBeanContainer) obj;

return (null == this.children) ? (null == other.children) : this.children.equals(other.children);
}

@Override
public String toString() {
return (null == this.children) ?
"FooJavaBeanContainer(null)" :
("FooJavaBeanContainer(" + this.children.toString() + ")");
}
}
3 changes: 3 additions & 0 deletions src/test/scala/fm/serializer/TestSerializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -514,9 +514,12 @@ trait TestSerializer[BYTES] extends FunSuite with Matchers with AppendedClues {

test("FooJavaBeanContainer") {
val container: FooJavaBeanContainer = new FooJavaBeanContainer()
container.setChildren(new java.util.ArrayList())

val bytes: BYTES = serialize(container)
val container2: FooJavaBeanContainer = deserialize[FooJavaBeanContainer](bytes)

container shouldBe container2
}

//===============================================================================================
Expand Down
15 changes: 8 additions & 7 deletions src/test/scala/fm/serializer/protobuf/TestProtobufRaw.scala
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,12 @@ final class TestProtobufRaw extends FunSuite with Matchers {
check(List(1,2,3,4,5,6), Array(1, 2, 3, 4, 5, 6))
check(List(-1,-2,-3,-4,-5,-6), Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -2, -1, -1, -1, -1, -1, -1, -1, -1, 1, -3, -1, -1, -1, -1, -1, -1, -1, -1, 1, -4, -1, -1, -1, -1, -1, -1, -1, -1, 1, -5, -1, -1, -1, -1, -1, -1, -1, -1, 1, -6, -1, -1, -1, -1, -1, -1, -1, -1, 1))
}

test("writeRawCollection Signed Int") {
implicit val serializer = Primitive.signedInt

check(List(1,2,3,4,5,6), Array(2, 4, 6, 8, 10, 12))
check(List(-1,-2,-3,-4,-5,-6), Array(1, 3, 5, 7, 9, 11))
}

// TODO: This serializer doesn't get picked up properly, so just re-tests fixedInt
// test("writeRawCollection Signed Int") {
// implicit val serializer = Primitive.signedInt
//
// check(List(1,2,3,4,5,6), Array(2, 4, 6, 8, 10, 12))
// check(List(-1,-2,-3,-4,-5,-6), Array(1, 3, 5, 7, 9, 11))
// }
}