-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #278 from altoo-ag/wip-277-scala3-enum-nvo
Adds Scala 3 enum serializer
- Loading branch information
Showing
14 changed files
with
185 additions
and
7 deletions.
There are no files selected for viewing
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
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
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
18 changes: 18 additions & 0 deletions
18
...alization/src/main/scala-3/io/altoo/akka/serialization/kryo/ScalaVersionSerializers.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package io.altoo.akka.serialization.kryo | ||
|
||
import com.esotericsoftware.kryo.Kryo | ||
import io.altoo.akka.serialization.kryo.serializer.scala.{ScalaCollectionSerializer, ScalaEnumNameSerializer, ScalaImmutableMapSerializer} | ||
|
||
private[kryo] object ScalaVersionSerializers { | ||
def mapAndSet(kryo: Kryo): Unit = { | ||
kryo.addDefaultSerializer(classOf[scala.collection.MapFactory[_root_.scala.collection.Map]], classOf[ScalaImmutableMapSerializer]) | ||
} | ||
|
||
def iterable(kryo: Kryo): Unit = { | ||
kryo.addDefaultSerializer(classOf[scala.collection.Iterable[_]], classOf[ScalaCollectionSerializer]) | ||
} | ||
|
||
def enums(kryo: Kryo): Unit = { | ||
kryo.addDefaultSerializer(classOf[scala.runtime.EnumValue], classOf[ScalaEnumNameSerializer[scala.runtime.EnumValue]]) | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
...scala-3/io/altoo/akka/serialization/kryo/serializer/scala/ScalaCollectionSerializer.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* ***************************************************************************** | ||
* Copyright 2012 Roman Levenstein | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* **************************************************************************** | ||
*/ | ||
|
||
package io.altoo.akka.serialization.kryo.serializer.scala | ||
|
||
import com.esotericsoftware.kryo.io.{Input, Output} | ||
import com.esotericsoftware.kryo.{Kryo, Serializer} | ||
|
||
/** | ||
* Generic serializer for traversable collections | ||
* | ||
* @author romix | ||
*/ | ||
class ScalaCollectionSerializer() extends Serializer[Iterable[_]] { | ||
|
||
override def read(kryo: Kryo, input: Input, typ: Class[_ <: Iterable[_]]): Iterable[_] = { | ||
val len = input.readInt(true) | ||
val inst = kryo.newInstance(typ) | ||
val coll = inst.iterableFactory.newBuilder[Any] | ||
|
||
var i = 0 | ||
while (i < len) { | ||
coll += kryo.readClassAndObject(input) | ||
i += 1 | ||
} | ||
coll.result() | ||
} | ||
|
||
override def write(kryo: Kryo, output: Output, obj: Iterable[_]): Unit = { | ||
val collection: Iterable[_] = obj | ||
val len = collection.size | ||
output.writeInt(len, true) | ||
collection.foreach { (e: Any) => kryo.writeClassAndObject(output, e) } | ||
} | ||
} | ||
|
26 changes: 26 additions & 0 deletions
26
...n/scala-3/io/altoo/akka/serialization/kryo/serializer/scala/ScalaEnumNameSerializer.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package io.altoo.akka.serialization.kryo.serializer.scala | ||
|
||
import com.esotericsoftware.kryo.{Kryo, Serializer} | ||
import com.esotericsoftware.kryo.io.{Input, Output} | ||
|
||
import scala.runtime.EnumValue | ||
|
||
/** Serializes enums using the enum's name. This prevents invalidating previously serialized bytes when the enum order changes */ | ||
class ScalaEnumNameSerializer[T <: EnumValue] extends Serializer[T] { | ||
|
||
def read(kryo: Kryo, input: Input, typ: Class[_ <: T]): T = { | ||
val clazz = kryo.readClass(input).getType | ||
val name = input.readString() | ||
// using value instead of ordinal to make serialization more stable, e.g. allowing reordering without breaking compatibility | ||
clazz.getDeclaredMethod("valueOf", classOf[String]).invoke(null, name).asInstanceOf[T] | ||
} | ||
|
||
def write(kryo: Kryo, output: Output, obj: T): Unit = { | ||
val enumClass = obj.getClass.getSuperclass | ||
val productPrefixMethod = obj.getClass.getDeclaredMethod("productPrefix") | ||
if (!productPrefixMethod.canAccess(obj)) productPrefixMethod.setAccessible(true) | ||
val name = productPrefixMethod.invoke(obj).asInstanceOf[String] | ||
kryo.writeClass(output, enumClass) | ||
output.writeString(name) | ||
} | ||
} |
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
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
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
49 changes: 49 additions & 0 deletions
49
...cala-3/io/altoo/akka/serialization/kryo/serializer/scala/ScalaEnumSerializationTest.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package io.altoo.akka.serialization.kryo.serializer.scala | ||
|
||
import com.esotericsoftware.kryo.util.{DefaultClassResolver, ListReferenceResolver} | ||
import io.altoo.akka.serialization.kryo.testkit.{AbstractKryoTest, KryoSerializationTesting} | ||
import org.scalatest.flatspec.AnyFlatSpec | ||
import org.scalatest.matchers.should.Matchers | ||
import io.altoo.akka.serialization.kryo.ScalaVersionSerializers | ||
|
||
object ScalaEnumSerializationTest { | ||
enum Sample(val name: String, val value: Int) { | ||
case A extends Sample("a", 1) | ||
case B extends Sample("b", 2) | ||
case C extends Sample("c", 3) | ||
} | ||
|
||
case class EmbeddedEnum(sample: Sample) { | ||
def this() = this(null) | ||
} | ||
} | ||
|
||
class ScalaEnumSerializationTest extends AnyFlatSpec with Matchers with KryoSerializationTesting { | ||
import ScalaEnumSerializationTest._ | ||
|
||
val kryo = new ScalaKryo(new DefaultClassResolver(), new ListReferenceResolver()) | ||
kryo.setRegistrationRequired(false) | ||
kryo.addDefaultSerializer(classOf[scala.runtime.EnumValue], new ScalaEnumNameSerializer[scala.runtime.EnumValue]) | ||
|
||
|
||
behavior of "Kryo serialization" | ||
|
||
it should "reoundtrip enum" in { | ||
kryo.setRegistrationRequired(false) | ||
|
||
testSerializationOf(Sample.B) | ||
} | ||
|
||
it should "reoundtrip external enum" in { | ||
kryo.setRegistrationRequired(false) | ||
|
||
testSerializationOf(io.altoo.external.ExternalEnum.A) | ||
} | ||
|
||
it should "reoundtrip embedded enum" in { | ||
kryo.setRegistrationRequired(false) | ||
kryo.register(classOf[EmbeddedEnum], 46) | ||
|
||
testSerializationOf(EmbeddedEnum(Sample.C)) | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...test/scala-3/io/altoo/akka/serialization/kryo/serializer/scala/ScalaVersionRegistry.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package io.altoo.akka.serialization.kryo.serializer.scala | ||
|
||
import com.esotericsoftware.kryo.Kryo | ||
|
||
object ScalaVersionRegistry { | ||
final val immutableHashMapImpl = "scala.collection.immutable.HashMap" | ||
final val immutableHashSetImpl = "scala.collection.immutable.HashSet" | ||
|
||
def registerHashMap(kryo: Kryo): Unit = { | ||
kryo.register(classOf[scala.collection.immutable.HashMap[_, _]], 40) | ||
} | ||
|
||
def registerHashSet(kryo: Kryo): Unit = { | ||
kryo.register(classOf[scala.collection.immutable.HashSet[_]], 41) | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
akka-kryo-serialization/src/test/scala-3/io/altoo/external/ExternalEnum.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.altoo.external | ||
|
||
import io.altoo.akka.serialization.kryo.serializer.scala.ScalaEnumSerializationTest.Sample | ||
|
||
enum ExternalEnum(val name: String) { | ||
case A extends ExternalEnum("a") | ||
} |
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
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