From b6fd8f952ff7a35f36793cd1dcb5479b832d0561 Mon Sep 17 00:00:00 2001 From: Thomas Heigl Date: Fri, 16 Dec 2022 12:53:09 +0100 Subject: [PATCH] Add additional safe serializers for commonly used JDK classes (#930) Adds serializers for the following classes: - java.net.URI - java.util.UUID - java.util.regex.Pattern - java.util.concurrent.atomic.AtomicBoolean - java.util.concurrent.atomic.AtomicInteger - java.util.concurrent.atomic.AtomicLong - java.util.concurrent.atomic.AtomicReference --- .../kryo/serializers/DefaultSerializers.java | 114 +++++++++++++++++- .../serializers/DefaultSerializersTest.java | 84 ++++++++++++- 2 files changed, 193 insertions(+), 5 deletions(-) diff --git a/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java b/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java index 471dad3d3..e74a891d2 100644 --- a/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java +++ b/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java @@ -34,6 +34,8 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; import java.sql.Time; @@ -58,7 +60,13 @@ import java.util.TimeZone; import java.util.TreeMap; import java.util.TreeSet; +import java.util.UUID; import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; /** Contains many serializer classes that are provided by {@link Kryo#addDefaultSerializer(Class, Class) default}. * @author Nathan Sweet */ @@ -900,6 +908,7 @@ public List copy (Kryo kryo, List original) { } } + /** Serializer for {@link BitSet} */ public static class BitSetSerializer extends Serializer { public void write (Kryo kryo, Output output, BitSet set) { long[] values = set.toLongArray(); @@ -910,12 +919,113 @@ public void write (Kryo kryo, Output output, BitSet set) { public BitSet read (Kryo kryo, Input input, Class type) { int length = input.readVarInt(true); long[] values = input.readLongs(length); - BitSet set = BitSet.valueOf(values); - return set; + return BitSet.valueOf(values); } public BitSet copy (Kryo kryo, BitSet original) { return BitSet.valueOf(original.toLongArray()); } } + + /** Serializer for {@link Pattern} */ + public static class PatternSerializer extends ImmutableSerializer { + public void write (final Kryo kryo, final Output output, final Pattern pattern) { + output.writeString(pattern.pattern()); + output.writeInt(pattern.flags(), true); + } + + public Pattern read (final Kryo kryo, final Input input, final Class patternClass) { + String regex = input.readString(); + int flags = input.readInt(true); + return Pattern.compile(regex, flags); + } + } + + /** Serializer for {@link URI} */ + public static class URISerializer extends ImmutableSerializer { + public void write (Kryo kryo, Output output, URI uri) { + output.writeString(uri.toString()); + } + + public URI read (Kryo kryo, Input input, Class uriClass) { + try { + return new URI(input.readString()); + } catch (URISyntaxException ex) { + throw new KryoException(ex); + } + } + } + + /** Serializer for {@link UUID} */ + public static class UUIDSerializer extends ImmutableSerializer { + public void write (Kryo kryo, Output output, UUID uuid) { + output.writeLong(uuid.getMostSignificantBits()); + output.writeLong(uuid.getLeastSignificantBits()); + } + + public UUID read (final Kryo kryo, final Input input, final Class uuidClass) { + return new UUID(input.readLong(), input.readLong()); + } + } + + /** Serializer for {@link AtomicBoolean} */ + public static class AtomicBooleanSerializer extends Serializer { + public void write (Kryo kryo, Output output, AtomicBoolean object) { + output.writeBoolean(object.get()); + } + + public AtomicBoolean read (Kryo kryo, Input input, Class type) { + return new AtomicBoolean(input.readBoolean()); + } + + public AtomicBoolean copy (Kryo kryo, AtomicBoolean original) { + return new AtomicBoolean(original.get()); + } + } + + /** Serializer for {@link AtomicInteger} */ + public static class AtomicIntegerSerializer extends Serializer { + public void write (Kryo kryo, Output output, AtomicInteger object) { + output.writeInt(object.get()); + } + + public AtomicInteger read (Kryo kryo, Input input, Class type) { + return new AtomicInteger(input.readInt()); + } + + public AtomicInteger copy (Kryo kryo, AtomicInteger original) { + return new AtomicInteger(original.get()); + } + } + + /** Serializer for {@link AtomicLong} */ + public static class AtomicLongSerializer extends Serializer { + public void write (Kryo kryo, Output output, AtomicLong object) { + output.writeLong(object.get()); + } + + public AtomicLong read (Kryo kryo, Input input, Class type) { + return new AtomicLong(input.readLong()); + } + + public AtomicLong copy (Kryo kryo, AtomicLong original) { + return new AtomicLong(original.get()); + } + } + + /** Serializer for {@link AtomicReference} */ + public static class AtomicReferenceSerializer extends Serializer { + public void write (Kryo kryo, Output output, AtomicReference object) { + kryo.writeClassAndObject(output, object.get()); + } + + public AtomicReference read (Kryo kryo, Input input, Class type) { + final Object value = kryo.readClassAndObject(input); + return new AtomicReference(value); + } + + public AtomicReference copy (Kryo kryo, AtomicReference original) { + return new AtomicReference<>(kryo.copy(original.get())); + } + } } diff --git a/test/com/esotericsoftware/kryo/serializers/DefaultSerializersTest.java b/test/com/esotericsoftware/kryo/serializers/DefaultSerializersTest.java index 3b72cd4b3..9dda49922 100644 --- a/test/com/esotericsoftware/kryo/serializers/DefaultSerializersTest.java +++ b/test/com/esotericsoftware/kryo/serializers/DefaultSerializersTest.java @@ -29,6 +29,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; @@ -43,6 +44,12 @@ import java.util.Locale; import java.util.PriorityQueue; import java.util.TimeZone; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; import org.junit.jupiter.api.Test; import org.objenesis.strategy.StdInstantiatorStrategy; @@ -518,19 +525,90 @@ void testArraysAsListDeepCopy () throws Exception { @Test void testURLSerializer () throws Exception { - kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); kryo.register(URL.class); roundTrip(42, new URL("https://github.com/EsotericSoftware/kryo")); roundTrip(78, new URL("https://github.com:443/EsotericSoftware/kryo/pulls?utf8=%E2%9C%93&q=is%3Apr")); } + @Test + void testURISerializer () throws Exception { + kryo.register(URI.class, new DefaultSerializers.URISerializer()); + + roundTrip(42, new URI("https://github.com/EsotericSoftware/kryo")); + roundTrip(78, new URI("https://github.com:443/EsotericSoftware/kryo/pulls?utf8=%E2%9C%93&q=is%3Apr")); + } + + @Test + void testUUIDSerializer () { + kryo.register(UUID.class, new DefaultSerializers.UUIDSerializer()); + + roundTrip(17, UUID.fromString("e58ed763-928c-4155-bee9-fdbaaadc15f3")); + } + + @Test + void testPatternSerializer () { + kryo.register(Pattern.class, new DefaultSerializers.PatternSerializer()); + + roundTrip(4, Pattern.compile(".", Pattern.DOTALL)); + roundTrip(4, Pattern.compile(".")); + } + + @Test + void testAtomicBooleanSerializer () { + kryo.register(AtomicBoolean.class, new DefaultSerializers.AtomicBooleanSerializer()); + + roundTrip(2, new AtomicBoolean(true)); + roundTrip(2, new AtomicBoolean(false)); + } + + @Test + void testAtomicIntegerSerializer () { + kryo.register(AtomicInteger.class, new DefaultSerializers.AtomicIntegerSerializer()); + + roundTrip(5, new AtomicInteger()); + roundTrip(5, new AtomicInteger(0)); + roundTrip(5, new AtomicInteger(1)); + roundTrip(5, new AtomicInteger(-1)); + } + + @Test + void testAtomicLongSerializer () { + kryo.register(AtomicLong.class, new DefaultSerializers.AtomicLongSerializer()); + + roundTrip(9, new AtomicLong()); + roundTrip(9, new AtomicLong(0)); + roundTrip(9, new AtomicLong(1)); + roundTrip(9, new AtomicLong(-1)); + } + + @Test + void testAtomicReferenceSerializer () { + kryo.register(AtomicReference.class, new DefaultSerializers.AtomicReferenceSerializer()); + + roundTrip(2, new AtomicReference<>()); + roundTrip(3, new AtomicReference<>(1L)); + } + protected void doAssertEquals(Object object1, Object object2) { if (object1 instanceof PriorityQueue && object2 instanceof PriorityQueue) { final PriorityQueue q1 = (PriorityQueue) object1; final PriorityQueue q2 = (PriorityQueue) object2; - super.doAssertEquals(q1.peek(), q2.peek()); - super.doAssertEquals(q1.toArray(), q2.toArray()); + super.doAssertEquals(q1.peek(), q2.peek()); + super.doAssertEquals(q1.toArray(), q2.toArray()); + } else if (object1 instanceof Pattern && object2 instanceof Pattern) { + final Pattern q1 = (Pattern)object1; + final Pattern q2 = (Pattern)object2; + super.doAssertEquals(q1.pattern(), q2.pattern()); + super.doAssertEquals(q1.flags(), q2.flags()); + } else if (object1 instanceof AtomicBoolean && object2 instanceof AtomicBoolean) { + super.doAssertEquals(((AtomicBoolean)object1).get(), ((AtomicBoolean)object2).get()); + } else if (object1 instanceof AtomicInteger && object2 instanceof AtomicInteger) { + super.doAssertEquals(((AtomicInteger)object1).get(), ((AtomicInteger)object2).get()); + } else if (object1 instanceof AtomicLong && object2 instanceof AtomicLong) { + super.doAssertEquals(((AtomicLong)object1).get(), ((AtomicLong)object2).get()); + } else if (object1 instanceof AtomicReference && object2 instanceof AtomicReference) { + super.doAssertEquals(((AtomicReference)object1).get(), ((AtomicReference)object2).get()); } else { super.doAssertEquals(object1, object2); }