GeezerVison is showing github.com — Add KryoUtils · voidframework/voidframework@d6c9ea4
File treeExpand file treeCollapse file tree Open diff view settings main/java/dev/voidframework/core test/java/dev/voidframework/core/utils
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboard +38Lines changed: 38 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package dev.voidframework.core.kryo; | | 2 | + | | 3 | +import com.esotericsoftware.kryo.Kryo; | | 4 | +import com.esotericsoftware.kryo.io.Input; | | 5 | +import com.esotericsoftware.kryo.io.Output; | | 6 | +import com.esotericsoftware.kryo.serializers.ImmutableSerializer; | | 7 | + | | 8 | +import java.util.List; | | 9 | + | | 10 | +/** | | 11 | + * Kryo serializer for Java {@code List}. | | 12 | + * | | 13 | + * @since 1.11.0 | | 14 | + */ | | 15 | +public final class ListSerializer extends ImmutableSerializer<List<Object>> { | | 16 | + | | 17 | + @Override | | 18 | + public void write(final Kryo kryo, final Output output, final List<Object> object) { | | 19 | + | | 20 | + output.writeInt(object.size(), true); | | 21 | + for (final Object obj : object) { | | 22 | + kryo.writeClassAndObject(output, obj); | | 23 | + } | | 24 | + } | | 25 | + | | 26 | + @Override | | 27 | + public List<Object> read(final Kryo kryo, final Input input, final Class<? extends List<Object>> type) { | | 28 | + | | 29 | + final int size = input.readInt(true); | | 30 | + | | 31 | + final Object[] objectArray = new Object[size]; | | 32 | + for (int idx = 0; idx < size; idx += 1) { | | 33 | + objectArray[idx] = kryo.readClassAndObject(input); | | 34 | + } | | 35 | + | | 36 | + return List.of(objectArray); | | 37 | + } | | 38 | +} |
Collapse file Copy file name to clipboard +38Lines changed: 38 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package dev.voidframework.core.kryo; | | 2 | + | | 3 | +import com.esotericsoftware.kryo.Kryo; | | 4 | +import com.esotericsoftware.kryo.io.Input; | | 5 | +import com.esotericsoftware.kryo.io.Output; | | 6 | +import com.esotericsoftware.kryo.serializers.ImmutableSerializer; | | 7 | + | | 8 | +import java.util.Set; | | 9 | + | | 10 | +/** | | 11 | + * Kryo serializer for Java {@code Set}. | | 12 | + * | | 13 | + * @since 1.11.0 | | 14 | + */ | | 15 | +public final class SetSerializer extends ImmutableSerializer<Set<Object>> { | | 16 | + | | 17 | + @Override | | 18 | + public void write(final Kryo kryo, final Output output, final Set<Object> object) { | | 19 | + | | 20 | + output.writeInt(object.size(), true); | | 21 | + for (final Object obj : object) { | | 22 | + kryo.writeClassAndObject(output, obj); | | 23 | + } | | 24 | + } | | 25 | + | | 26 | + @Override | | 27 | + public Set<Object> read(final Kryo kryo, final Input input, final Class<? extends Set<Object>> type) { | | 28 | + | | 29 | + final int size = input.readInt(true); | | 30 | + | | 31 | + final Object[] objectArray = new Object[size]; | | 32 | + for (int idx = 0; idx < size; idx += 1) { | | 33 | + objectArray[idx] = kryo.readClassAndObject(input); | | 34 | + } | | 35 | + | | 36 | + return Set.of(objectArray); | | 37 | + } | | 38 | +} |
Collapse file Copy file name to clipboard +159Lines changed: 159 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package dev.voidframework.core.utils; | | 2 | + | | 3 | +import com.esotericsoftware.kryo.Kryo; | | 4 | +import com.esotericsoftware.kryo.io.ByteBufferOutputStream; | | 5 | +import com.esotericsoftware.kryo.io.Input; | | 6 | +import com.esotericsoftware.kryo.io.Output; | | 7 | +import dev.voidframework.core.kryo.ListSerializer; | | 8 | +import dev.voidframework.core.kryo.SetSerializer; | | 9 | + | | 10 | +import java.math.BigDecimal; | | 11 | +import java.math.BigInteger; | | 12 | +import java.util.ArrayList; | | 13 | +import java.util.EnumMap; | | 14 | +import java.util.EnumSet; | | 15 | +import java.util.HashMap; | | 16 | +import java.util.HashSet; | | 17 | +import java.util.LinkedHashMap; | | 18 | +import java.util.LinkedHashSet; | | 19 | +import java.util.List; | | 20 | +import java.util.Optional; | | 21 | +import java.util.Set; | | 22 | + | | 23 | +/** | | 24 | + * Kryo serializer utility methods. | | 25 | + * | | 26 | + * @since 1.11.0 | | 27 | + */ | | 28 | +public final class KryoUtils { | | 29 | + | | 30 | + private static final Kryo KRYO = new Kryo(); | | 31 | + | | 32 | + static { | | 33 | + | | 34 | + // Configures Kryo | | 35 | + KRYO.setRegistrationRequired(false); | | 36 | + | | 37 | + // Registers primitive types | | 38 | + KRYO.register(boolean.class); | | 39 | + KRYO.register(double.class); | | 40 | + KRYO.register(float.class); | | 41 | + KRYO.register(int.class); | | 42 | + KRYO.register(long.class); | | 43 | + | | 44 | + // Registers types | | 45 | + KRYO.register(ArrayList.class); | | 46 | + KRYO.register(BigDecimal.class); | | 47 | + KRYO.register(BigInteger.class); | | 48 | + KRYO.register(Boolean.class); | | 49 | + KRYO.register(Class.class); | | 50 | + KRYO.register(Double.class); | | 51 | + KRYO.register(EnumMap.class); | | 52 | + KRYO.register(EnumSet.class); | | 53 | + KRYO.register(Float.class); | | 54 | + KRYO.register(HashMap.class); | | 55 | + KRYO.register(HashSet.class); | | 56 | + KRYO.register(Integer.class); | | 57 | + KRYO.register(LinkedHashMap.class); | | 58 | + KRYO.register(LinkedHashSet.class); | | 59 | + KRYO.register(Long.class); | | 60 | + KRYO.register(Optional.class); | | 61 | + KRYO.register(String.class); | | 62 | + | | 63 | + // Adds new serializers (for types with no-args constructor) | | 64 | + KRYO.addDefaultSerializer(List.class, ListSerializer.class); | | 65 | + KRYO.addDefaultSerializer(Set.class, SetSerializer.class); | | 66 | + } | | 67 | + | | 68 | + /** | | 69 | + * Default constructor. | | 70 | + * | | 71 | + * @since 1.11.0 | | 72 | + */ | | 73 | + private KryoUtils() { | | 74 | + | | 75 | + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); | | 76 | + } | | 77 | + | | 78 | + /** | | 79 | + * Deserialize an object. | | 80 | + * | | 81 | + * @param serializedContent An array of bytes representing a serialized object | | 82 | + * @param classType The object class type | | 83 | + * @param <T> The type of the Java object | | 84 | + * @return Deserialized object | | 85 | + * @since 1.11.0 | | 86 | + */ | | 87 | + public static <T> T deserialize(final byte[] serializedContent, final Class<T> classType) { | | 88 | + | | 89 | + if (serializedContent == null) { | | 90 | + return null; | | 91 | + } | | 92 | + | | 93 | + final Input input = new Input(serializedContent); | | 94 | + return KRYO.readObjectOrNull(input, classType); | | 95 | + } | | 96 | + | | 97 | + /** | | 98 | + * Serialize an object. | | 99 | + * | | 100 | + * @param object The object to serialize | | 101 | + * @return An array of bytes representing the serialized object | | 102 | + * @since 1.11.0 | | 103 | + */ | | 104 | + public static byte[] serialize(final Object object) { | | 105 | + | | 106 | + final Output output = new Output(new ByteBufferOutputStream()); | | 107 | + final Class<?> classType = object != null ? object.getClass() : Object.class; | | 108 | + | | 109 | + KRYO.writeObjectOrNull(output, object, classType); | | 110 | + return output.toBytes(); | | 111 | + } | | 112 | + | | 113 | + /** | | 114 | + * Deserialize an object. | | 115 | + * This method will return {@code null} if an exception is thrown. | | 116 | + * | | 117 | + * @param serializedContent An array of bytes representing a serialized object | | 118 | + * @param classType The object class type | | 119 | + * @param <T> The type of the Java object | | 120 | + * @return Deserialized object | | 121 | + * @since 1.11.0 | | 122 | + */ | | 123 | + public static <T> T deserializeWithoutException(final byte[] serializedContent, final Class<T> classType) { | | 124 | + | | 125 | + try { | | 126 | + return deserialize(serializedContent, classType); | | 127 | + } catch (final Exception ignore) { | | 128 | + return null; | | 129 | + } | | 130 | + } | | 131 | + | | 132 | + /** | | 133 | + * Serialize an object. | | 134 | + * This method will return {@code null} if an exception is thrown. | | 135 | + * | | 136 | + * @param object The object to serialize | | 137 | + * @return An array of bytes representing the serialized object | | 138 | + * @since 1.11.0 | | 139 | + */ | | 140 | + public static byte[] serializeWithoutException(final Object object) { | | 141 | + | | 142 | + try { | | 143 | + return serialize(object); | | 144 | + } catch (final Exception ignore) { | | 145 | + return null; | | 146 | + } | | 147 | + } | | 148 | + | | 149 | + /** | | 150 | + * Gets the Kryo instance. | | 151 | + * | | 152 | + * @return Kryo instance | | 153 | + * @since 1.11.0 | | 154 | + */ | | 155 | + public static Kryo kryo() { | | 156 | + | | 157 | + return KRYO; | | 158 | + } | | 159 | +} |
Collapse file Copy file name to clipboard +138Lines changed: 138 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +package dev.voidframework.core.utils; | | 2 | + | | 3 | +import com.esotericsoftware.kryo.Kryo; | | 4 | +import org.junit.jupiter.api.Assertions; | | 5 | +import org.junit.jupiter.api.MethodOrderer; | | 6 | +import org.junit.jupiter.api.Test; | | 7 | +import org.junit.jupiter.api.TestMethodOrder; | | 8 | +import org.junit.jupiter.params.ParameterizedTest; | | 9 | +import org.junit.jupiter.params.provider.Arguments; | | 10 | +import org.junit.jupiter.params.provider.MethodSource; | | 11 | + | | 12 | +import java.lang.reflect.Constructor; | | 13 | +import java.lang.reflect.InvocationTargetException; | | 14 | +import java.math.BigDecimal; | | 15 | +import java.util.List; | | 16 | +import java.util.Optional; | | 17 | +import java.util.Set; | | 18 | +import java.util.stream.Stream; | | 19 | + | | 20 | +@TestMethodOrder(MethodOrderer.MethodName.class) | | 21 | +final class KryoUtilsTest { | | 22 | + | | 23 | + static Stream<Arguments> getDeserializeArguments() { | | 24 | + return Stream.of( | | 25 | + Arguments.of(new byte[]{1, -14, 20}, Integer.class, 1337), | | 26 | + Arguments.of(new byte[]{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, -28}, String.class, "Hello World"), | | 27 | + Arguments.of(new byte[]{1, 12, 2, 10, 0}, Optional.class, Optional.of(BigDecimal.TEN)), | | 28 | + Arguments.of(new byte[]{1, 12, 3, 8, 0, 0}, Optional.class, Optional.of(BigDecimal.valueOf(2048))), | | 29 | + Arguments.of(new byte[]{1, 2, 3, 65, 112, 112, 108, -27, 3, 66, 97, 110, 97, 110, -31}, List.class, List.of("Apple", "Banana")), | | 30 | + Arguments.of(new byte[]{1, 1, 3, 65, 112, 112, 108, -27}, Set.class, Set.of("Apple")), | | 31 | + Arguments.of(new byte[]{0}, Set.class, null)); | | 32 | + } | | 33 | + | | 34 | + static Stream<Arguments> getDeserializeWithoutExceptionArguments() { | | 35 | + return Stream.of( | | 36 | + Arguments.of(new byte[]{1, -14, 20}, Integer.class, 1337), | | 37 | + Arguments.of(new byte[]{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, -28}, String.class, "Hello World"), | | 38 | + Arguments.of(new byte[]{1, 12, 2, 10, 0}, Optional.class, Optional.of(BigDecimal.TEN)), | | 39 | + Arguments.of(new byte[]{1, 12, 3, 8, 0, 0}, Optional.class, Optional.of(BigDecimal.valueOf(2048))), | | 40 | + Arguments.of(new byte[]{1, 12, 3, 8, 0, 0}, TestDTO.class, null), // "null" because no serializer registered for "TestDTO" | | 41 | + Arguments.of(new byte[]{1, 2, 3, 65, 112, 112, 108, -27, 3, 66, 97, 110, 97, 110, -31}, List.class, List.of("Apple", "Banana")), | | 42 | + Arguments.of(new byte[]{1, 1, 3, 65, 112, 112, 108, -27}, Set.class, Set.of("Apple")), | | 43 | + Arguments.of(new byte[]{0}, Set.class, null)); | | 44 | + } | | 45 | + | | 46 | + static Stream<Arguments> getSerializeArguments() { | | 47 | + return Stream.of( | | 48 | + Arguments.of(1337, new byte[]{1, -14, 20}), | | 49 | + Arguments.of("Hello World", new byte[]{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, -28}), | | 50 | + Arguments.of(Optional.of(BigDecimal.TEN), new byte[]{1, 12, 2, 10, 0}), | | 51 | + Arguments.of(Optional.of(BigDecimal.valueOf(2048)), new byte[]{1, 12, 3, 8, 0, 0}), | | 52 | + Arguments.of(new TestDTO("Clémence"), new byte[]{1, -119, 67, 108, -61, -87, 109, 101, 110, 99, 101}), | | 53 | + Arguments.of(List.of("Apple", "Banana"), new byte[]{1, 2, 3, 65, 112, 112, 108, -27, 3, 66, 97, 110, 97, 110, -31}), | | 54 | + Arguments.of(Set.of("Apple"), new byte[]{1, 1, 3, 65, 112, 112, 108, -27}), | | 55 | + Arguments.of(null, new byte[]{0})); | | 56 | + } | | 57 | + | | 58 | + @Test | | 59 | + void constructor() throws NoSuchMethodException { | | 60 | + | | 61 | + // Act | | 62 | + final Constructor<KryoUtils> constructor = KryoUtils.class.getDeclaredConstructor(); | | 63 | + constructor.setAccessible(true); | | 64 | + | | 65 | + final InvocationTargetException exception = Assertions.assertThrows(InvocationTargetException.class, constructor::newInstance); | | 66 | + | | 67 | + // Assert | | 68 | + Assertions.assertNotNull(exception.getCause()); | | 69 | + Assertions.assertEquals("This is a utility class and cannot be instantiated", exception.getCause().getMessage()); | | 70 | + } | | 71 | + | | 72 | + @Test | | 73 | + void kryo() { | | 74 | + | | 75 | + // Act | | 76 | + final Kryo kryo = KryoUtils.kryo(); | | 77 | + | | 78 | + // Assert | | 79 | + Assertions.assertNotNull(kryo); | | 80 | + } | | 81 | + | | 82 | + @ParameterizedTest | | 83 | + @MethodSource("getDeserializeArguments") | | 84 | + void deserialize(final byte[] toDeserialize, final Class<?> outputClassType, final Object expected) { | | 85 | + | | 86 | + // Act | | 87 | + final Object deserializedObject = KryoUtils.deserialize(toDeserialize, outputClassType); | | 88 | + | | 89 | + // Assert | | 90 | + Assertions.assertEquals(expected, deserializedObject); | | 91 | + } | | 92 | + | | 93 | + @ParameterizedTest | | 94 | + @MethodSource("getDeserializeWithoutExceptionArguments") | | 95 | + void deserializeWithoutException(final byte[] toDeserialize, final Class<?> outputClassType, final Object expected) { | | 96 | + | | 97 | + // Act | | 98 | + final Object deserializedObject = KryoUtils.deserializeWithoutException(toDeserialize, outputClassType); | | 99 | + | | 100 | + // Assert | | 101 | + Assertions.assertEquals(expected, deserializedObject); | | 102 | + } | | 103 | + | | 104 | + @ParameterizedTest | | 105 | + @MethodSource("getSerializeArguments") | | 106 | + void serialize(final Object toSerialize, final byte[] expected) { | | 107 | + | | 108 | + // Act | | 109 | + final byte[] serializedContent = KryoUtils.serialize(toSerialize); | | 110 | + | | 111 | + // Assert | | 112 | + Assertions.assertArrayEquals(expected, serializedContent); | | 113 | + } | | 114 | + | | 115 | + @ParameterizedTest | | 116 | + @MethodSource("getSerializeArguments") | | 117 | + void serializeWithoutException(final Object toSerialize, final byte[] expected) { | | 118 | + | | 119 | + // Act | | 120 | + final byte[] serializedContent = KryoUtils.serializeWithoutException(toSerialize); | | 121 | + | | 122 | + // Assert | | 123 | + Assertions.assertArrayEquals(expected, serializedContent); | | 124 | + } | | 125 | + | | 126 | + /** | | 127 | + * Test DTO. | | 128 | + */ | | 129 | + public static final class TestDTO { | | 130 | + | | 131 | + final String name; | | 132 | + | | 133 | + public TestDTO(final String name) { | | 134 | + | | 135 | + this.name = name; | | 136 | + } | | 137 | + } | | 138 | +} |
|