From 16f1d6d3dd4ef779bed2a32d5d08582befb95b31 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Fri, 26 Nov 2021 09:47:40 -0800 Subject: [PATCH 1/9] feat(ByteBuffer-Serializer): Add ByteBuffer serializer implementation. * add tests * add benchmarks * add dummy ObjectStream impl for InMemory Serializer * add ByteBuffer as target serializer to serializers --- .../typehandlerlibrary/THLBenchmark.java | 190 ++++++ .../gson/GsonPersistedDataReader.java | 12 +- .../gson/GsonPersistedDataWriter.java | 6 + .../protobuf/ProtobufDataReader.java | 7 + .../protobuf/ProtobufDataWriter.java | 6 + .../TypeHandlerLibrary/build.gradle.kts | 2 + .../serializers/PersistedDataReader.java | 3 + .../serializers/PersistedDataWriter.java | 3 + .../persistence/serializers/Serializer.java | 80 ++- .../typeHandling/bytebuffer/BBArrayType.java | 54 ++ .../typeHandling/bytebuffer/BBType.java | 40 ++ .../bytebuffer/ByteBufferDataReader.java | 27 + .../bytebuffer/ByteBufferDataWriter.java | 27 + .../bytebuffer/ByteBufferPersistedData.java | 245 ++++++++ .../ByteBufferPersistedDataArray.java | 358 +++++++++++ .../ByteBufferPersistedSerializer.java | 296 +++++++++ .../inMemory/AbstractPersistedData.java | 3 +- .../typeHandling/inMemory/InMemoryReader.java | 36 ++ .../typeHandling/inMemory/InMemoryWriter.java | 36 ++ .../ByteBufferSerializerTest.java | 571 ++++++++++++++++++ .../FullSerializationDeserializationTest.java | 235 +++++++ 21 files changed, 2214 insertions(+), 23 deletions(-) create mode 100644 engine/src/jmh/java/org/terasology/benchmark/typehandlerlibrary/THLBenchmark.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataReader.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataWriter.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java create mode 100644 subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/ByteBufferSerializerTest.java create mode 100644 subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java diff --git a/engine/src/jmh/java/org/terasology/benchmark/typehandlerlibrary/THLBenchmark.java b/engine/src/jmh/java/org/terasology/benchmark/typehandlerlibrary/THLBenchmark.java new file mode 100644 index 00000000000..fd5dd2d232a --- /dev/null +++ b/engine/src/jmh/java/org/terasology/benchmark/typehandlerlibrary/THLBenchmark.java @@ -0,0 +1,190 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.benchmark.typehandlerlibrary; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.reflections.Reflections; +import org.terasology.engine.persistence.typeHandling.TypeHandlerLibraryImpl; +import org.terasology.engine.persistence.typeHandling.gson.GsonPersistedDataReader; +import org.terasology.engine.persistence.typeHandling.gson.GsonPersistedDataSerializer; +import org.terasology.engine.persistence.typeHandling.gson.GsonPersistedDataWriter; +import org.terasology.engine.persistence.typeHandling.protobuf.ProtobufDataReader; +import org.terasology.engine.persistence.typeHandling.protobuf.ProtobufDataWriter; +import org.terasology.engine.persistence.typeHandling.protobuf.ProtobufPersistedDataSerializer; +import org.terasology.persistence.serializers.Serializer; +import org.terasology.persistence.typeHandling.TypeHandlerLibrary; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferDataReader; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferDataWriter; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferPersistedSerializer; +import org.terasology.persistence.typeHandling.inMemory.InMemoryPersistedDataSerializer; +import org.terasology.persistence.typeHandling.inMemory.InMemoryReader; +import org.terasology.persistence.typeHandling.inMemory.InMemoryWriter; +import org.terasology.reflection.TypeInfo; + +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 1) +@Fork(1) +@Measurement(iterations = 1) +public class THLBenchmark { + + + @Benchmark + public Optional ser(SerializerState state, Data data) { + return state.serializer.serialize(data.value, data.type); + } + + @Benchmark + public Optional serde(SerializerState state, Data data) { + Optional o = state.serializer.serialize(data.value, data.type); + return state.serializer.deserialize(data.type, o.get()); + } + + + public enum Serializers { + GSON((thl) -> { + Gson gson = new Gson(); + return new Serializer(thl, new GsonPersistedDataSerializer(), new GsonPersistedDataWriter(gson), + new GsonPersistedDataReader(gson)); + }), + IN_MEMORY((thl) -> new Serializer(thl, new InMemoryPersistedDataSerializer(), new InMemoryWriter(), + new InMemoryReader())), + PROTOBUF((thl) -> new Serializer(thl, new ProtobufPersistedDataSerializer(), new ProtobufDataWriter(), + new ProtobufDataReader())), + BYTEBUFFER((thl) -> new Serializer(thl, new ByteBufferPersistedSerializer(), new ByteBufferDataWriter(), + new ByteBufferDataReader())); + private final Function creator; + + Serializers(Function creator) { + this.creator = creator; + } + } + + public enum Datas { + BOOLEAN(Boolean.TYPE, true), + BYTE(Byte.TYPE, (byte) 1), + INT(Integer.TYPE, 1), + LONG(Long.TYPE, 1L), + FLOAT(Float.TYPE, 1F), + DOUBLE(Double.TYPE, 1D), + CHAR(Character.TYPE, 'c'), + STRING(String.class, "string"), + BO_ARRAY(boolean[].class, new boolean[]{true}), + BY_ARRAY(byte[].class, new byte[]{(byte) 1}), + I_ARRAY(int[].class, new int[]{1}), + L_ARRAY(long[].class, new long[]{1L}), + F_ARRAY(float[].class, new float[]{1F}), + D_ARRAY(double[].class, new double[]{1D}), + C_ARRAY(char[].class, new char[]{'c'}), + S_ARRAY(String[].class, new String[]{"foo", "bar"}), + ENUM(ExampleEnum.class, ExampleEnum.ONE), + S_LIST(new TypeInfo>() { + }, Lists.newArrayList("foo", "bar")), + S_SET(new TypeInfo>() { + }, Sets.newHashSet("foo", "bar")), + E_SET(new TypeInfo>() { + }, EnumSet.of(ExampleEnum.ONE, ExampleEnum.THREE)); + + private final TypeInfo type; + private final Object obj; + + Datas(Class clazz, Object obj) { + this.type = TypeInfo.of(clazz); + this.obj = obj; + } + + Datas(TypeInfo type, Object obj) { + this.type = type; + this.obj = obj; + } + } + + enum ExampleEnum { + ONE, + TWO, + THREE + } + + @State(Scope.Thread) + public static class Data { + @Param({"BOOLEAN", +// "BYTE", + "INT", +// "LONG", +// "FLOAT", +// "DOUBLE", +// "CHAR", +// "STRING", + "BO_ARRAY", + "BY_ARRAY", + "I_ARRAY", +// "L_ARRAY", +// "F_ARRAY", +// "D_ARRAY", +// "C_ARRAY", + "S_ARRAY", +// "ENUM", + "S_LIST", +// "S_SET", + "E_SET"}) + private static Datas datas; + + TypeInfo type; + Object value; + + @Setup(Level.Iteration) + public void setup() { + type = datas.type; + value = datas.obj; + } + } + + @State(Scope.Benchmark) + public static class THLState { + private static Reflections reflections; + private static TypeHandlerLibrary typeHandlerLibrary; + + @Setup(Level.Trial) + public static void setup() { + reflections = new Reflections(THLBenchmark.class.getClassLoader()); + typeHandlerLibrary = TypeHandlerLibraryImpl.withReflections(reflections); + } + } + + @State(Scope.Benchmark) + public static class SerializerState { + @Param({"GSON", "IN_MEMORY", "PROTOBUF", "BYTEBUFFER"}) + private static Serializers serializers; + + private Serializer serializer; + + @Setup(Level.Trial) + public void setup(THLState state) { + serializer = serializers.creator.apply(THLState.typeHandlerLibrary); + } + } + + +} diff --git a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataReader.java b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataReader.java index 68e2f3b214c..de2f6b2d0c1 100644 --- a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataReader.java +++ b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataReader.java @@ -4,12 +4,13 @@ package org.terasology.engine.persistence.typeHandling.gson; import com.google.gson.Gson; -import com.google.gson.JsonObject; +import com.google.gson.JsonElement; import org.terasology.persistence.serializers.PersistedDataReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -30,13 +31,18 @@ public GsonPersistedDataReader(Gson gson) { @Override public GsonPersistedData read(InputStream inputStream) throws IOException { - JsonObject jsonObject = gson.fromJson(new InputStreamReader(inputStream), JsonObject.class); + JsonElement jsonObject = gson.fromJson(new InputStreamReader(inputStream), JsonElement.class); return new GsonPersistedData(jsonObject); } @Override public GsonPersistedData read(byte[] byteBuffer) throws IOException { - JsonObject jsonObject = gson.fromJson(new String(byteBuffer, charset), JsonObject.class); + JsonElement jsonObject = gson.fromJson(new String(byteBuffer, charset), JsonElement.class); return new GsonPersistedData(jsonObject); } + + @Override + public GsonPersistedData read(ByteBuffer byteBuffer) throws IOException { + throw new UnsupportedOperationException("Idk how to parse this."); + } } diff --git a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataWriter.java b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataWriter.java index e005e7164a0..aa33251fb02 100644 --- a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataWriter.java +++ b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/gson/GsonPersistedDataWriter.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -40,4 +41,9 @@ public void writeTo(GsonPersistedData data, OutputStream outputStream) throws IO } } + + @Override + public void writeTo(GsonPersistedData data, ByteBuffer byteBuffer) throws IOException { + byteBuffer.put(writeBytes(data)); + } } diff --git a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataReader.java b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataReader.java index 8b50c601272..8ec858162e0 100644 --- a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataReader.java +++ b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataReader.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.ByteBuffer; public class ProtobufDataReader implements PersistedDataReader { @Override @@ -22,4 +23,10 @@ public ProtobufPersistedData read(byte[] byteBuffer) throws InvalidProtocolBuffe EntityData.Value value = EntityData.Value.parseFrom(byteBuffer); return new ProtobufPersistedData(value); } + + @Override + public ProtobufPersistedData read(ByteBuffer byteBuffer) throws InvalidProtocolBufferException { + EntityData.Value value = EntityData.Value.parseFrom(byteBuffer); + return new ProtobufPersistedData(value); + } } diff --git a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataWriter.java b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataWriter.java index fce33737756..9d2e00a8cb7 100644 --- a/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataWriter.java +++ b/engine/src/main/java/org/terasology/engine/persistence/typeHandling/protobuf/ProtobufDataWriter.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.nio.ByteBuffer; public class ProtobufDataWriter implements PersistedDataWriter { @Override @@ -19,4 +20,9 @@ public byte[] writeBytes(ProtobufPersistedData data) { public void writeTo(ProtobufPersistedData data, OutputStream outputStream) throws IOException { data.getValue().writeTo(CodedOutputStream.newInstance(outputStream)); } + + @Override + public void writeTo(ProtobufPersistedData data, ByteBuffer byteBuffer) throws IOException { + byteBuffer.put(data.getValue().toByteArray()); + } } diff --git a/subsystems/TypeHandlerLibrary/build.gradle.kts b/subsystems/TypeHandlerLibrary/build.gradle.kts index 4fc9250f370..2ee43ea2ac8 100644 --- a/subsystems/TypeHandlerLibrary/build.gradle.kts +++ b/subsystems/TypeHandlerLibrary/build.gradle.kts @@ -31,6 +31,8 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") testImplementation("org.mockito:mockito-inline:3.12.4") + testImplementation("com.google.guava:guava:31.0-jre") + testImplementation("org.mockito:mockito-junit-jupiter:3.12.4") } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataReader.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataReader.java index 13374d77c71..ea219ca8528 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataReader.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataReader.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.ByteBuffer; /** * Read {@link PersistedData} from files, stream, buffer, etc. @@ -15,4 +16,6 @@ public interface PersistedDataReader { T read(InputStream inputStream) throws IOException; T read(byte[] byteBuffer) throws IOException; + + T read(ByteBuffer byteBuffer) throws IOException; } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataWriter.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataWriter.java index 000b07cafe6..0d099621c31 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataWriter.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/PersistedDataWriter.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.nio.ByteBuffer; /** * Write {@link PersistedData} to files, stream, buffer, etc. @@ -16,4 +17,6 @@ public interface PersistedDataWriter { byte[] writeBytes(T data); void writeTo(T data, OutputStream outputStream) throws IOException; + + void writeTo(T data, ByteBuffer byteBuffer) throws IOException; } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/Serializer.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/Serializer.java index e4a0586cc04..caf0385885f 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/Serializer.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/serializers/Serializer.java @@ -12,15 +12,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.util.Optional; /** - * The abstract class that all serializers derive from. It by default provides the ability to serialize/deserialize an - * object to/from a {@link PersistedData} using the given {@link PersistedDataSerializer}. + * The abstract class that all serializers derive from. It by default provides the ability to serialize/deserialize an object to/from a + * {@link PersistedData} using the given {@link PersistedDataSerializer}. *

- * Implementors simply need to specify the type of {@link PersistedDataSerializer} to use and can provide convenience - * methods that use {@link #serializeToPersisted(Object, TypeInfo)} and {@link #deserializeFromPersisted(PersistedData, - * TypeInfo)}. + * Implementors simply need to specify the type of {@link PersistedDataSerializer} to use and can provide convenience methods that use + * {@link #serializeToPersisted(Object, TypeInfo)} and {@link #deserializeFromPersisted(PersistedData, TypeInfo)}. */ public final class Serializer { @@ -32,7 +32,7 @@ public final class Serializer { private final PersistedDataReader reader; public Serializer(TypeHandlerLibrary typeHandlerLibrary, PersistedDataSerializer persistedDataSerializer, - PersistedDataWriter writer, PersistedDataReader reader) { + PersistedDataWriter writer, PersistedDataReader reader) { this.typeHandlerLibrary = typeHandlerLibrary; this.persistedDataSerializer = persistedDataSerializer; this.writer = writer; @@ -40,15 +40,14 @@ public Serializer(TypeHandlerLibrary typeHandlerLibrary, PersistedDataSerializer } /** - * Serializes the given object to a {@link PersistedData} using the stored {@link #persistedDataSerializer} by - * loading a {@link org.terasology.persistence.typeHandling.TypeHandler TypeHandler} from the {@link - * #typeHandlerLibrary}. + * Serializes the given object to a {@link PersistedData} using the stored {@link #persistedDataSerializer} by loading a {@link + * org.terasology.persistence.typeHandling.TypeHandler TypeHandler} from the {@link #typeHandlerLibrary}. * * @param object The object to serialize. * @param typeInfo A {@link TypeInfo} specifying the type of the object to serialize. * @param The type of the object to serialize. - * @return A {@link PersistedData}, if the serialization was successful. Serialization usually fails only because an - * appropriate type handler could not be found for the given type. + * @return A {@link PersistedData}, if the serialization was successful. Serialization usually fails only because an appropriate type + * handler could not be found for the given type. */ private Optional serializeToPersisted(T object, TypeInfo typeInfo) { return typeHandlerLibrary.getTypeHandler(typeInfo) @@ -56,16 +55,15 @@ private Optional serializeToPersisted(T object, TypeInfo typeInfo) { } /** - * Deserializes an object of the given type from a {@link PersistedData} using the stored {@link - * #persistedDataSerializer} by loading a {@link org.terasology.persistence.typeHandling.TypeHandler TypeHandler} - * from the {@link #typeHandlerLibrary}. + * Deserializes an object of the given type from a {@link PersistedData} using the stored {@link #persistedDataSerializer} by loading a + * {@link org.terasology.persistence.typeHandling.TypeHandler TypeHandler} from the {@link #typeHandlerLibrary}. * * @param data The {@link PersistedData} containing the serialized representation of the object. * @param typeInfo The {@link TypeInfo} specifying the type to deserialize the object as. * @param The type to deserialize the object as. - * @return The deserialized object of type {@link T}, if the deserialization was successful. Deserialization usually - * fails when an appropriate type handler could not be found for the type {@link T} or if the - * serialized object representation in {@code data} does not represent an object of type {@link T}. + * @return The deserialized object of type {@link T}, if the deserialization was successful. Deserialization usually fails when an + * appropriate type handler could not be found for the type {@link T} or if the serialized object representation in + * {@code data} does not represent an object of type {@link T}. */ private Optional deserializeFromPersisted(D data, TypeInfo typeInfo) { return typeHandlerLibrary.getTypeHandler(typeInfo).flatMap(typeHandler -> typeHandler.deserialize(data)); @@ -107,14 +105,32 @@ public Optional deserialize(TypeInfo type, byte[] bytes) { return Optional.empty(); } + /** + * Deserialize an object of type from {@link ByteBuffer} + * + * @param type The {@link TypeInfo} specifying the type to deserialize the object as. + * @param byteBuffer The ByteBuffer contains data for deserialization. + * @param The type to deserialize the object as. + * @return an Object if deserialization is success, empty otherwise + */ + public Optional deserialize(TypeInfo type, ByteBuffer byteBuffer) { + try { + D persistedData = reader.read(byteBuffer); + return deserializeFromPersisted(persistedData, type); + } catch (IOException e) { + logger.error("Cannot deserialize type [" + type + "]", e); + } + return Optional.empty(); + } + /** * Serializes the given object to a ByteArray * * @param object The object to serialize. * @param type A {@link TypeInfo} specifying the type of the object to serialize. * @param The type of the object to serialize. - * @return A ByteArray, if the serialization was successful. Serialization usually fails only because an appropriate - * type handler could not be found for the given type. + * @return A ByteArray, if the serialization was successful. Serialization usually fails only because an appropriate type handler could + * not be found for the given type. */ public Optional serialize(T object, TypeInfo type) { Optional persistedData = serializeToPersisted(object, type); @@ -146,4 +162,30 @@ public void serialize(T object, TypeInfo type, OutputStream outputStream) logger.error("Cannot serialize [{}]", type); } } + + /** + * Serializes and write the given object to {@link ByteBuffer} + * + * @param object The object to serialize. + * @param type A {@link TypeInfo} specifying the type of the object to serialize. + * @param byteBuffer A {@link ByteBuffer} which will used for writing. + * @param The type of the object to serialize. + */ + public void serialize(T object, TypeInfo type, ByteBuffer byteBuffer) { + Optional persistedData = serializeToPersisted(object, type); + if (persistedData.isPresent()) { + try { + writer.writeTo(persistedData.get(), byteBuffer); + } catch (IOException e) { + logger.error("Cannot serialize [" + type + "]", e); + } + } else { + logger.error("Cannot serialize [{}]", type); + } + } + + @Override + public String toString() { + return String.format("Serializer with %s", persistedDataSerializer.getClass().getSimpleName()); + } } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java new file mode 100644 index 00000000000..2dd77883b2d --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java @@ -0,0 +1,54 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +/** + * Type codes for types. + */ +public enum BBArrayType { + BOOLEAN(0), + FLOAT(1), + DOUBLE(2), + LONG(3), + INTEGER(4), + STRING(5), + VALUE(6); + + private final byte code; + + BBArrayType(int code) { + this.code = (byte) code; + } + + public byte getCode() { + return code; + } + + public BBType getPrimitiveType() { + switch (this) { + case BOOLEAN: + return BBType.BOOLEAN; + case FLOAT: + return BBType.FLOAT; + case DOUBLE: + return BBType.DOUBLE; + case LONG: + return BBType.LONG; + case INTEGER: + return BBType.INTEGER; + case STRING: + return BBType.STRING; + } + return null; + } + + public static BBArrayType parse(byte code) { + for (BBArrayType type : values()) { + if (type.code == code) { + return type; + } + } + return null; + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java new file mode 100644 index 00000000000..fb7a6f5b410 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java @@ -0,0 +1,40 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +/** + * Type codes for types. + */ +public enum BBType { + NULL(0), + BOOLEAN(1), + FLOAT(2), + DOUBLE(3), + LONG(4), + INTEGER(5), + STRING(6), + BYTES(7), + BYTEBUFFER(8), + ARRAY(9), + VALUEMAP(10); + + private final byte code; + + BBType(int code) { + this.code = (byte) code; + } + + public byte getCode() { + return code; + } + + public static BBType parse(byte code) { + for (BBType type : values()) { + if (type.code == code) { + return type; + } + } + return null; + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataReader.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataReader.java new file mode 100644 index 00000000000..cee648c158b --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataReader.java @@ -0,0 +1,27 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +import org.terasology.persistence.serializers.PersistedDataReader; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +public class ByteBufferDataReader implements PersistedDataReader { + @Override + public ByteBufferPersistedData read(InputStream inputStream) throws IOException { + throw new UnsupportedOperationException("Idk which size are you using"); + } + + @Override + public ByteBufferPersistedData read(byte[] byteBuffer) throws IOException { + return new ByteBufferPersistedData(ByteBuffer.wrap(byteBuffer).asReadOnlyBuffer()); + } + + @Override + public ByteBufferPersistedData read(ByteBuffer byteBuffer) throws IOException { + return new ByteBufferPersistedData(byteBuffer); + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataWriter.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataWriter.java new file mode 100644 index 00000000000..2a77623cc29 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferDataWriter.java @@ -0,0 +1,27 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +import org.terasology.persistence.serializers.PersistedDataWriter; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public class ByteBufferDataWriter implements PersistedDataWriter { + @Override + public byte[] writeBytes(ByteBufferPersistedData data) { + return data.getData().array(); + } + + @Override + public void writeTo(ByteBufferPersistedData data, OutputStream outputStream) throws IOException { + throw new UnsupportedOperationException("Idk which size are you using"); + } + + @Override + public void writeTo(ByteBufferPersistedData data, ByteBuffer byteBuffer) throws IOException { + byteBuffer.put(data.getAsByteBuffer()); + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java new file mode 100644 index 00000000000..ee8559caa69 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java @@ -0,0 +1,245 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +import org.terasology.persistence.typeHandling.DeserializationException; +import org.terasology.persistence.typeHandling.PersistedData; +import org.terasology.persistence.typeHandling.PersistedDataArray; +import org.terasology.persistence.typeHandling.PersistedDataMap; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +/** + * ByteBuffer-backed persisted data. + *

+ *     Header:
+ *     1 byte - header.
+ *
+ *     Types:
+ *      NULL(0) - Null value
+ *      BOOLEAN(1) - boolean - not packed. takes 1 byte.
+ *      FLOAT(2)
+ *      DOUBLE(3)
+ *      LONG(4)
+ *      INTEGER(5)
+ *      STRING(6) - UTF-8. header: 1 int - length
+ *      BYTES(7) - header: 1 int - length
+ *      BYTEBUFFER(8) - just passthru this bytebuffer //TODO
+ *      ARRAY(9) -
+ *      VALUEMAP(10) -
+ * 
+ */ +public class ByteBufferPersistedData implements PersistedData { + protected final ByteBuffer byteBuffer; + protected final int position; + private final BBType type; + private final boolean typeForced; + + public ByteBufferPersistedData(ByteBuffer byteBuffer, int position, byte type) { + this.byteBuffer = byteBuffer; + this.position = position; + this.type = BBType.parse(type); + this.typeForced = true; + } + + public ByteBufferPersistedData(ByteBuffer byteBuffer) { + this.byteBuffer = byteBuffer; + this.position = byteBuffer.position(); + this.type = BBType.parse(byteBuffer.get()); + this.typeForced = false; + } + + public ByteBufferPersistedData(ByteBuffer byteBuffer, int position) { + this.byteBuffer = byteBuffer; + this.position = position; + byteBuffer.position(position); + this.type = BBType.parse(byteBuffer.get()); + this.typeForced = false; + } + + public ByteBufferPersistedData(ByteBuffer byteBuffer, byte type) { + this(byteBuffer, byteBuffer.position(), type); + } + + + public ByteBuffer getData() { + return byteBuffer; + } + + @Override + public String getAsString() { + if (!isString()) { + throw new ClassCastException("it is not string"); + } + resetPosition(); + int size = byteBuffer.getInt(); + byte[] bytes = new byte[size]; + byteBuffer.get(bytes); + return new String(bytes, StandardCharsets.UTF_8); + } + + private void resetPosition() { + byteBuffer.position(position + (typeForced ? 0 : 1)); + } + + @Override + public double getAsDouble() { + if (!isNumber()) { + throw new ClassCastException("It is not number"); + } + resetPosition(); + switch (type) { + case INTEGER: + return getData().getInt(); + case FLOAT: + return getData().getFloat(); + case DOUBLE: + return getData().getDouble(); + case LONG: + return (double) getData().getLong(); + } + throw new ClassCastException("Data is not a number"); + } + + @Override + public float getAsFloat() { + if (!isNumber()) { + throw new ClassCastException("It is not number"); + } + resetPosition(); + switch (type) { + case INTEGER: + return (float) getData().getInt(); + case FLOAT: + return getData().getFloat(); + case DOUBLE: + return (float) getData().getDouble(); + case LONG: + return (float) getData().getLong(); + } + throw new ClassCastException("Data is not a number"); + } + + @Override + public int getAsInteger() { + if (!isNumber()) { + throw new ClassCastException("It is not number"); + } + resetPosition(); + switch (type) { + case INTEGER: + return getData().getInt(); + case FLOAT: + return (int) getData().getFloat(); + case DOUBLE: + return (int) getData().getDouble(); + case LONG: + return (int) getData().getLong(); + } + throw new ClassCastException("Data is not a number"); + } + + @Override + public long getAsLong() { + if (!isNumber()) { + throw new ClassCastException("It is not number"); + } + resetPosition(); + switch (type) { + case INTEGER: + return getData().getInt(); + case FLOAT: + return (long) getData().getFloat(); + case DOUBLE: + return (long) getData().getDouble(); + case LONG: + return getData().getLong(); + } + throw new ClassCastException("Data is not a number"); + } + + + @Override + public boolean getAsBoolean() { + if (!isBoolean()) { + throw new ClassCastException("It is not boolean"); + } + resetPosition(); + return byteBuffer.get() != 0; // Don't Packed booleans + } + + @Override + public byte[] getAsBytes() { + if (!isBytes()) { + throw new DeserializationException("it is not bytes or bytebuffer"); + } + resetPosition(); + int size = byteBuffer.getInt(); + byte[] bytes = new byte[size]; + byteBuffer.get(bytes); + return bytes; + } + + @Override + public ByteBuffer getAsByteBuffer() { + if (!isBytes()) { + throw new DeserializationException("it is not bytes or bytebuffer"); + } + resetPosition(); + return ByteBuffer.wrap(getAsBytes()); + } + + @Override + public PersistedDataArray getAsArray() { + if (!isArray()) { + throw new IllegalStateException("it is not array"); + } + byteBuffer.position(position); + return new ByteBufferPersistedDataArray(byteBuffer); + } + + @Override + public PersistedDataMap getAsValueMap() { + throw new IllegalStateException("Not implemeneted yet"); + } + + @Override + public boolean isString() { + return type == BBType.STRING; + } + + @Override + public boolean isNumber() { + return type == BBType.FLOAT + || type == BBType.DOUBLE + || type == BBType.INTEGER + || type == BBType.LONG; + } + + @Override + public boolean isBoolean() { + return type == BBType.BOOLEAN; + } + + @Override + public boolean isBytes() { + return type == BBType.BYTES || type == BBType.BYTEBUFFER; + } + + @Override + public boolean isArray() { + return type == BBType.ARRAY; + } + + @Override + public boolean isValueMap() { + return type == BBType.VALUEMAP; + } + + @Override + public boolean isNull() { + return type == BBType.NULL; + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java new file mode 100644 index 00000000000..4754692c7cc --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java @@ -0,0 +1,358 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +import gnu.trove.list.TDoubleList; +import gnu.trove.list.TFloatList; +import gnu.trove.list.TIntList; +import gnu.trove.list.TLongList; +import gnu.trove.list.array.TDoubleArrayList; +import gnu.trove.list.array.TFloatArrayList; +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.list.array.TLongArrayList; +import org.terasology.persistence.typeHandling.DeserializationException; +import org.terasology.persistence.typeHandling.PersistedData; +import org.terasology.persistence.typeHandling.PersistedDataArray; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +public class ByteBufferPersistedDataArray extends ByteBufferPersistedData implements PersistedDataArray { + + private final BBArrayType arrayType; + private final int size; + + public ByteBufferPersistedDataArray(ByteBuffer byteBuffer) { + super(byteBuffer); + arrayType = BBArrayType.parse(byteBuffer.get()); + size = byteBuffer.getInt(); + } + + @Override + public int size() { + return size; + } + + @Override + public PersistedData getArrayItem(int index) { + BBType primitiveType = arrayType.getPrimitiveType(); + if (primitiveType != null) { + return new ByteBufferPersistedData(byteBuffer, calculateIndex(index), primitiveType.getCode()); + } else { + return new ByteBufferPersistedData(byteBuffer, calculateIndex(index)); + } + } + + @Override + public boolean isNumberArray() { + return arrayType == BBArrayType.FLOAT + || arrayType == BBArrayType.DOUBLE + || arrayType == BBArrayType.INTEGER + || arrayType == BBArrayType.LONG; + } + + @Override + public boolean isBooleanArray() { + return arrayType == BBArrayType.BOOLEAN; + } + + @Override + public boolean isStringArray() { + return arrayType == BBArrayType.STRING; + } + + @Override + public List getAsStringArray() { + List list = new ArrayList<>(size()); + for (int i = 0; i < size(); i++) { + list.add(getArrayItem(i).getAsString()); + } + return list; + } + + @Override + public String getAsString() { + if (isStringArray()) { + if (size() == 1) { + return getArrayItem(0).getAsString(); + } else { + throw new IllegalStateException("String array have size != 1"); + } + } else { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isString()) { + return data.getAsString(); + } + } + throw new ClassCastException("it is not string array"); + } + } + + + @Override + public double getAsDouble() { + if (isNumberArray()) { + if (size() == 1) { + return getArrayItem(0).getAsDouble(); + } else { + throw new IllegalStateException("Number array have size != 1"); + } + } else { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isNumber()) { + return data.getAsDouble(); + } + } + throw new ClassCastException("it is not number array"); + } + } + + @Override + public float getAsFloat() { + if (isNumberArray()) { + if (size() == 1) { + return getArrayItem(0).getAsFloat(); + } else { + throw new IllegalStateException("Number array have size != 1"); + } + } else { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isNumber()) { + return data.getAsFloat(); + } + } + throw new ClassCastException("it is not number array"); + } + } + + @Override + public int getAsInteger() { + if (isNumberArray()) { + if (size() == 1) { + return getArrayItem(0).getAsInteger(); + } else { + throw new IllegalStateException("Number array have size != 1"); + } + } else { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isNumber()) { + return data.getAsInteger(); + } + } + throw new ClassCastException("it is not number array"); + } + } + + @Override + public long getAsLong() { + if (isNumberArray()) { + if (size() == 1) { + return getArrayItem(0).getAsLong(); + } else { + throw new IllegalStateException("Number array have size != 1"); + } + } else { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isNumber()) { + return data.getAsLong(); + } + } + throw new ClassCastException("it is not number array"); + } + } + + @Override + public TDoubleList getAsDoubleArray() { + byteBuffer.position(position + 6); + TDoubleList list = new TDoubleArrayList(size()); + Iterator iter = typedIterator(arrayType.getPrimitiveType()); + while (iter.hasNext()) { + list.add(iter.next().getAsDouble()); + } + return list; + } + + @Override + public TFloatList getAsFloatArray() { + byteBuffer.position(position + 6); + TFloatList list = new TFloatArrayList(size()); + Iterator iter = typedIterator(arrayType.getPrimitiveType()); + while (iter.hasNext()) { + list.add(iter.next().getAsFloat()); + } + return list; + } + + @Override + public TIntList getAsIntegerArray() { + byteBuffer.position(position + 6); + TIntList list = new TIntArrayList(size()); + Iterator iter = typedIterator(arrayType.getPrimitiveType()); + while (iter.hasNext()) { + list.add(iter.next().getAsInteger()); + } + return list; + } + + @Override + public TLongList getAsLongArray() { + byteBuffer.position(position + 6); + TLongList list = new TLongArrayList(size()); + Iterator iter = typedIterator(arrayType.getPrimitiveType()); + while (iter.hasNext()) { + list.add(iter.next().getAsLong()); + } + return list; + } + + @Override + public boolean[] getAsBooleanArray() { + byteBuffer.position(position + 6); + int sizeInBytes = size() % 8 + 1; + byte[] bytes = new byte[sizeInBytes]; + byteBuffer.get(bytes); + boolean[] booleans = new boolean[size()]; + for (int i = 0; i < sizeInBytes; i++) { + for (int bi = 0; bi < 8; bi++) { + if (i * 8 + bi >= size()) { + break; + } + booleans[i * 8 + bi] = ((bytes[i] >> bi) & 1) == 1; + } + } + return booleans; + } + + @Override + public List getAsValueArray() { + byteBuffer.position(position + 6); + List data = new ArrayList<>(size()); + for (int i = 0; i < size(); i++) { + data.add(new ByteBufferPersistedData(byteBuffer, calculateIndex(i))); + } + return data; + } + + @Override + public Iterator iterator() { + return new Iterator() { + private int index; + + @Override + public boolean hasNext() { + return index < size; + } + + @Override + public PersistedData next() { + if (!hasNext()) { + throw new NoSuchElementException("iterator haven't something."); + } + PersistedData data = new ByteBufferPersistedData(byteBuffer, calculateIndex(index)); + index++; + return data; + } + }; + } + + private Iterator typedIterator(BBType type) { + return new Iterator() { + private int index; + + @Override + public boolean hasNext() { + return index < size; + } + + @Override + public PersistedData next() { + if (!hasNext()) { + throw new NoSuchElementException("iterator haven't something."); + } + PersistedData data = new ByteBufferPersistedData(byteBuffer, calculateIndex(index), type.getCode()); + index++; + return data; + } + }; + } + + private int calculateIndex(int index) { + switch (arrayType) { + case BOOLEAN: + return 6 + index % 8 + 1; + case FLOAT: + case INTEGER: + return 6 + index * 4; + case DOUBLE: + case LONG: + return 6 + index * 8; + case STRING: { + int pos = position + 6; + for (int i = 0; i < index; i++) { + pos += byteBuffer.getInt(pos) + 4; + } + return pos; + } + case VALUE: { + int pos = 0; + for (int i = 0; i < index; i++) { + pos += byteBuffer.getInt(position + 6 + i * 4); + } + int sizeArraySize = size() * 4; + return pos + position + 6 + sizeArraySize; + } + + } + throw new UnsupportedOperationException("IDK how it to do"); + } + + @Override + public boolean getAsBoolean() { + if (isBooleanArray()) { + if (size() == 1) { + return (byteBuffer.get(position + 6) & 1) == 1; + } else { + throw new IllegalStateException("boolean array have size != 1"); + } + } else { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isBoolean()) { + return data.getAsBoolean(); + } + } + throw new ClassCastException("it is not boolean array"); + } + } + + @Override + public byte[] getAsBytes() { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isBytes()) { + return data.getAsBytes(); + } + } + throw new DeserializationException("it is not bytes array"); + } + + @Override + public ByteBuffer getAsByteBuffer() { + if (size() == 1) { + PersistedData data = getArrayItem(0); + if (data.isBytes()) { + return data.getAsByteBuffer(); + } + } + throw new DeserializationException("it is not bytes array"); + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java new file mode 100644 index 00000000000..342c974bdd4 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java @@ -0,0 +1,296 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import gnu.trove.iterator.TDoubleIterator; +import gnu.trove.iterator.TFloatIterator; +import gnu.trove.iterator.TIntIterator; +import gnu.trove.iterator.TLongIterator; +import gnu.trove.list.array.TDoubleArrayList; +import gnu.trove.list.array.TFloatArrayList; +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.list.array.TLongArrayList; +import org.terasology.persistence.typeHandling.PersistedData; +import org.terasology.persistence.typeHandling.PersistedDataSerializer; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +/** + * TODO make direct serializer factory. + */ +public class ByteBufferPersistedSerializer implements PersistedDataSerializer { + + @Override + public PersistedData serialize(String value) { + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + ByteBuffer buffer = ByteBuffer.allocate(bytes.length + 1 + 4); + buffer.put(BBType.STRING.getCode()); + buffer.putInt(bytes.length); + buffer.put(bytes); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(String... values) { + return serializeStrings(Arrays.asList(values)); + } + + @Override + public PersistedData serializeStrings(Iterable value) { + List buffers = Lists.newArrayList(); + int byteSize = 0; + int size = 0; + for (String str : value) { + ByteBufferPersistedData serialize = (ByteBufferPersistedData) serialize(str); + byteSize += serialize.getData().array().length; + size++; + buffers.add(serialize); + } + if (size == 1) { + return buffers.get(0); + } + ByteBuffer buffer = ByteBuffer.allocate(byteSize + 4); + buffer.put(BBType.ARRAY.getCode()); + buffer.put(BBArrayType.STRING.getCode()); + buffer.putInt(size); + for (ByteBufferPersistedData data : buffers) { + data.getData().position(1); // remove header of string. + buffer.put(data.getData()); + } + buffer.rewind(); + return new ByteBufferPersistedDataArray(buffer); + } + + @Override + public PersistedData serialize(float value) { + ByteBuffer buffer = ByteBuffer.allocate(5); + buffer.put(BBType.FLOAT.getCode()); + buffer.putFloat(value); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(float... values) { + ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 4); + buffer.put(BBType.ARRAY.getCode()); + buffer.put(BBArrayType.FLOAT.getCode()); + buffer.putInt(values.length); + for (float value : values) { + buffer.putFloat(value); + } + buffer.rewind(); + return new ByteBufferPersistedDataArray(buffer); + } + + @Override + public PersistedData serialize(TFloatIterator value) { + TFloatArrayList data = new TFloatArrayList(); + while (value.hasNext()) { + data.add(value.next()); + } + return serialize(data.toArray()); + } + + @Override + public PersistedData serialize(int value) { + ByteBuffer buffer = ByteBuffer.allocate(5); + buffer.put(BBType.INTEGER.getCode()); + buffer.putInt(value); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(int... values) { + ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 4); + buffer.put(BBType.ARRAY.getCode()); + buffer.put(BBArrayType.INTEGER.getCode()); + buffer.putInt(values.length); + for (int value : values) { + buffer.putInt(value); + } + buffer.rewind(); + return new ByteBufferPersistedDataArray(buffer); + } + + @Override + public PersistedData serialize(TIntIterator value) { + TIntArrayList data = new TIntArrayList(); + while (value.hasNext()) { + data.add(value.next()); + } + return serialize(data.toArray()); + } + + @Override + public PersistedData serialize(long value) { + ByteBuffer buffer = ByteBuffer.allocate(9); + buffer.put(BBType.LONG.getCode()); + buffer.putLong(value); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(long... values) { + ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 8); + buffer.put(BBType.ARRAY.getCode()); + buffer.put(BBArrayType.LONG.getCode()); + buffer.putInt(values.length); + for (long value : values) { + buffer.putLong(value); + } + buffer.rewind(); + return new ByteBufferPersistedDataArray(buffer); + } + + @Override + public PersistedData serialize(TLongIterator value) { + TLongArrayList data = new TLongArrayList(); + while (value.hasNext()) { + data.add(value.next()); + } + return serialize(data.toArray()); + } + + @Override + public PersistedData serialize(boolean value) { + ByteBuffer buffer = ByteBuffer.allocate(9); + buffer.put(BBType.BOOLEAN.getCode()); + buffer.put(value ? (byte) 1 : (byte) 0); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(boolean... values) { + int size = values.length; + int sizeInBytes = size % 8 + 1; + ByteBuffer buffer = ByteBuffer.allocate(6 + sizeInBytes); + buffer.put(BBType.ARRAY.getCode()); + buffer.put(BBArrayType.BOOLEAN.getCode()); + buffer.putInt(size); + for (int i = 0; i < sizeInBytes; i++) { + byte valueByte = 0; + for (int bi = 0; bi < 8; bi++) { + if (i * 8 + bi >= size) { + break; + } + boolean b = values[i * 8 + bi]; + valueByte |= (b ? 1 : 0) << bi; + } + buffer.put(valueByte); + } + buffer.rewind(); + return new ByteBufferPersistedDataArray(buffer); + + } + + @Override + public PersistedData serialize(double value) { + ByteBuffer buffer = ByteBuffer.allocate(10); + buffer.put(BBType.DOUBLE.getCode()); + buffer.putDouble(value); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(double... values) { + ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 8); + buffer.put(BBType.ARRAY.getCode()); + buffer.put(BBArrayType.DOUBLE.getCode()); + buffer.putInt(values.length); + for (double value : values) { + buffer.putDouble(value); + } + buffer.rewind(); + return new ByteBufferPersistedDataArray(buffer); + } + + @Override + public PersistedData serialize(TDoubleIterator value) { + TDoubleArrayList data = new TDoubleArrayList(); + while (value.hasNext()) { + data.add(value.next()); + } + return serialize(data.toArray()); + } + + @Override + public PersistedData serialize(byte[] value) { + ByteBuffer buffer = ByteBuffer.allocate(value.length + 1 + 4); + buffer.put(BBType.BYTES.getCode()); + buffer.putInt(value.length); + buffer.put(value); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(ByteBuffer value) { + int size = value.array().length; + ByteBuffer buffer = ByteBuffer.allocate(1 + 4 + size); + buffer.put(BBType.BYTEBUFFER.getCode()); + buffer.putInt(size); + buffer.put(value); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } + + @Override + public PersistedData serialize(PersistedData... values) { + int bytes = 0; + int[] sizes = new int[values.length]; + for (int i = 0; i < values.length; i++) { + PersistedData data = values[i]; + ByteBufferPersistedData dataBytes = (ByteBufferPersistedData) data; + int length = dataBytes.byteBuffer.array().length; + bytes += length; + sizes[i] = length; + } + ByteBuffer buffer = ByteBuffer.allocate(6 + values.length * 4 + bytes); + buffer.put(BBType.ARRAY.getCode()); + buffer.put(BBArrayType.VALUE.getCode()); + buffer.putInt(values.length); + // Write data sizes + for (int size : sizes) { + buffer.putInt(size); + } + for (PersistedData data : values) { + ByteBufferPersistedData dataBytes = (ByteBufferPersistedData) data; + dataBytes.byteBuffer.rewind(); + buffer.put(dataBytes.byteBuffer); + } + buffer.rewind(); + return new ByteBufferPersistedDataArray(buffer); + } + + @Override + public PersistedData serialize(Iterable data) { + return serialize(Iterables.toArray(data, PersistedData.class)); + } + + @Override + public PersistedData serialize(Map data) { + return null; + } + + @Override + public PersistedData serializeNull() { + ByteBuffer buffer = ByteBuffer.allocate(1) + .put(BBType.NULL.getCode()); + buffer.rewind(); + return new ByteBufferPersistedData(buffer); + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/AbstractPersistedData.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/AbstractPersistedData.java index e40a230b229..27d08f0e71d 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/AbstractPersistedData.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/AbstractPersistedData.java @@ -7,9 +7,10 @@ import org.terasology.persistence.typeHandling.PersistedDataArray; import org.terasology.persistence.typeHandling.PersistedDataMap; +import java.io.Serializable; import java.nio.ByteBuffer; -public abstract class AbstractPersistedData implements PersistedData { +public abstract class AbstractPersistedData implements PersistedData, Serializable { @Override public String getAsString() { diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java new file mode 100644 index 00000000000..1c3381ac714 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java @@ -0,0 +1,36 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.inMemory; + +import org.terasology.persistence.serializers.PersistedDataReader; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.nio.ByteBuffer; + +public class InMemoryReader implements PersistedDataReader { + @Override + public AbstractPersistedData read(InputStream inputStream) throws IOException { + ObjectInputStream ois = new ObjectInputStream(inputStream); + try { + return (AbstractPersistedData) ois.readObject(); + } catch (ClassNotFoundException e) { + return null; // TODO + } + } + + @Override + public AbstractPersistedData read(byte[] byteBuffer) throws IOException { + return read(new ByteArrayInputStream(byteBuffer)); + } + + @Override + public AbstractPersistedData read(ByteBuffer byteBuffer) throws IOException { + byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + return read(bytes); + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java new file mode 100644 index 00000000000..7bc6f15e1d6 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java @@ -0,0 +1,36 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.inMemory; + +import org.terasology.persistence.serializers.PersistedDataWriter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public class InMemoryWriter implements PersistedDataWriter { + @Override + public byte[] writeBytes(AbstractPersistedData data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + writeTo(data, baos); // TODO! + } catch (IOException e) { + e.printStackTrace(); + } + return baos.toByteArray(); + } + + @Override + public void writeTo(AbstractPersistedData data, OutputStream outputStream) throws IOException { + ObjectOutputStream oos = new ObjectOutputStream(outputStream); + oos.writeObject(data); + } + + @Override + public void writeTo(AbstractPersistedData data, ByteBuffer byteBuffer) throws IOException { + byteBuffer.put(writeBytes(data)); + } +} diff --git a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/ByteBufferSerializerTest.java b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/ByteBufferSerializerTest.java new file mode 100644 index 00000000000..d3c53f63985 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/ByteBufferSerializerTest.java @@ -0,0 +1,571 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling; + +import gnu.trove.list.array.TDoubleArrayList; +import gnu.trove.list.array.TFloatArrayList; +import gnu.trove.list.array.TIntArrayList; +import gnu.trove.list.array.TLongArrayList; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.internal.util.collections.Sets; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferPersistedData; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferPersistedDataArray; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferPersistedSerializer; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; + + +class ByteBufferSerializerTest { + private static PersistedDataSerializer serializer = new ByteBufferPersistedSerializer(); + + public static Stream types() { + return Stream.of( + Arguments.of(serializer.serialize(1), + Sets.newSet(TypeGetter.INTEGER, TypeGetter.LONG, TypeGetter.FLOAT, TypeGetter.DOUBLE)), + Arguments.of(serializer.serialize(1L), + Sets.newSet(TypeGetter.INTEGER, TypeGetter.LONG, TypeGetter.FLOAT, TypeGetter.DOUBLE)), + Arguments.of(serializer.serialize(1F), + Sets.newSet(TypeGetter.INTEGER, TypeGetter.LONG, TypeGetter.FLOAT, TypeGetter.DOUBLE)), + Arguments.of(serializer.serialize(1D), + Sets.newSet(TypeGetter.INTEGER, TypeGetter.LONG, TypeGetter.FLOAT, TypeGetter.DOUBLE)), + + Arguments.of(serializer.serialize("foo"), // TODO + Sets.newSet(TypeGetter.STRING)), + + Arguments.of(serializer.serialize(new byte[]{(byte) 0xFF}), + Sets.newSet(TypeGetter.BYTE_BUFFER, TypeGetter.BYTES)), + Arguments.of(serializer.serialize(ByteBuffer.wrap(new byte[]{(byte) 0xFF})), + Sets.newSet(TypeGetter.BYTES, TypeGetter.BYTE_BUFFER)), + Arguments.of(serializer.serialize(true), + Sets.newSet(TypeGetter.BOOLEAN)) + ); + } + + @Test + void serializeString() { + PersistedData data = serializer.serialize("foo"); + + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + Assertions.assertTrue(data.isString()); + Assertions.assertEquals("foo", data.getAsString()); + + Assertions.assertFalse(data.isArray()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isNumber()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + Assertions.assertThrows(IllegalStateException.class, data::getAsArray); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + Assertions.assertThrows(ClassCastException.class, data::getAsInteger); + Assertions.assertThrows(ClassCastException.class, data::getAsLong); + Assertions.assertThrows(ClassCastException.class, data::getAsFloat); + Assertions.assertThrows(ClassCastException.class, data::getAsDouble); + } + + @Test + void serializeStrings() { + PersistedData data = serializer.serialize("foo", "bar"); + Assertions.assertEquals(ByteBufferPersistedDataArray.class, data.getClass()); + + Assertions.assertTrue(data.isArray()); + Assertions.assertEquals("foo", data.getAsArray().getArrayItem(0).getAsString()); + + Assertions.assertTrue(data.getAsArray().isStringArray()); + Assertions.assertEquals("foo", data.getAsArray().getAsStringArray().get(0)); + + Assertions.assertTrue(data.isArray()); + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsString); + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + Assertions.assertThrows(ClassCastException.class, data::getAsInteger); + Assertions.assertThrows(ClassCastException.class, data::getAsLong); + Assertions.assertThrows(ClassCastException.class, data::getAsFloat); + Assertions.assertThrows(ClassCastException.class, data::getAsDouble); + } + + //TODO remove it + public void template(PersistedData data) { + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isArray()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isNumber()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + Assertions.assertThrows(IllegalStateException.class, data::getAsArray); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + Assertions.assertThrows(ClassCastException.class, data::getAsInteger); + Assertions.assertThrows(ClassCastException.class, data::getAsLong); + Assertions.assertThrows(ClassCastException.class, data::getAsFloat); + Assertions.assertThrows(ClassCastException.class, data::getAsDouble); + } + + @Test + void serializeOneAsStrings() { + PersistedData data = serializer.serialize(new String[]{"foo"}); + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + + Assertions.assertEquals("foo", data.getAsString()); + } + + @Test + void serializeStringsIterable() { + PersistedData data = serializer.serializeStrings(Arrays.asList("foo", "bar")); + Assertions.assertEquals(ByteBufferPersistedDataArray.class, data.getClass()); + + Assertions.assertTrue(data.isArray()); + Assertions.assertEquals("foo", data.getAsArray().getArrayItem(0).getAsString()); + + Assertions.assertTrue(data.getAsArray().isStringArray()); + Assertions.assertEquals("foo", data.getAsArray().getAsStringArray().get(0)); + } + + @Test + void serializeOneAsStringsIterable() { + PersistedData data = serializer.serializeStrings(Collections.singleton("foo")); + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + + Assertions.assertEquals("foo", data.getAsString()); + } + + @Test + void serializeFloat() { + PersistedData data = serializer.serialize(1f); + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + checkIsNumber(data); + } + + @Test + void serializeFloats() { + PersistedData data = serializer.serialize(new float[]{1F}); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeTFloatIterator() { + PersistedData data = serializer.serialize(TFloatArrayList.wrap(new float[]{1F}).iterator()); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeInt() { + PersistedData data = serializer.serialize(1); + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + + checkIsNumber(data); + } + + @Test + void serializeInts() { + PersistedData data = serializer.serialize(new int[]{1}); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeTIntIterator() { + PersistedData data = serializer.serialize(TIntArrayList.wrap(new int[]{1}).iterator()); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeLong() { + PersistedData data = serializer.serialize(1L); + + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + + checkIsNumber(data); + } + + @Test + void serializeLongs() { + PersistedData data = serializer.serialize(new long[]{1L}); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeTLongIterator() { + PersistedData data = serializer.serialize(TLongArrayList.wrap(new long[]{1L}).iterator()); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeBoolean() { + boolean value = true; + PersistedData data = serializer.serialize(value); + + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + + Assertions.assertTrue(data.isBoolean()); + Assertions.assertEquals(value, data.getAsBoolean()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isArray()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isNumber()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + Assertions.assertThrows(IllegalStateException.class, data::getAsArray); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(ClassCastException.class, data::getAsInteger); + Assertions.assertThrows(ClassCastException.class, data::getAsLong); + Assertions.assertThrows(ClassCastException.class, data::getAsFloat); + Assertions.assertThrows(ClassCastException.class, data::getAsDouble); + } + + @Test + void serializeBooleans() { + PersistedData data = serializer.serialize(new boolean[]{true}); + + Assertions.assertEquals(ByteBufferPersistedDataArray.class, data.getClass()); + + Assertions.assertTrue(data.isArray()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertTrue(data.getAsArray().isBooleanArray()); + + Assertions.assertTrue(data.getAsBoolean()); + Assertions.assertArrayEquals(new boolean[]{true}, data.getAsArray().getAsBooleanArray()); + + + Assertions.assertEquals(ByteBufferPersistedData.class, data.getAsArray().getArrayItem(0).getClass()); + } + + + @Test + void serializeBooleansComplicated() { + boolean[] value = {true, true, true, false, true, false, true, false, true, true, true, false, true}; + PersistedData data = serializer.serialize(value); + + Assertions.assertEquals(ByteBufferPersistedDataArray.class, data.getClass()); + + Assertions.assertTrue(data.isArray()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(IllegalStateException.class, data::getAsBoolean); + + Assertions.assertArrayEquals(value, data.getAsArray().getAsBooleanArray()); + + Assertions.assertTrue(data.getAsArray().isBooleanArray()); + + + Assertions.assertEquals(ByteBufferPersistedData.class, data.getAsArray().getArrayItem(0).getClass()); + } + + @Test + void serializeDouble() { + PersistedData data = serializer.serialize(1D); + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + + checkIsNumber(data); + } + + @Test + void serializeDoubles() { + PersistedData data = serializer.serialize(new double[]{1D}); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeTDoubleIterator() { + PersistedData data = serializer.serialize(TDoubleArrayList.wrap(new double[]{1D}).iterator()); + checkNumberArray(data, ByteBufferPersistedDataArray.class, ByteBufferPersistedData.class); + } + + @Test + void serializeBytes() { + byte[] value = {(byte) 0xFF}; + PersistedData data = serializer.serialize(value); + + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + Assertions.assertTrue(data.isBytes()); + Assertions.assertArrayEquals(value, data.getAsBytes()); + Assertions.assertEquals(ByteBuffer.wrap(value), data.getAsByteBuffer()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isArray()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isNumber()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + Assertions.assertThrows(IllegalStateException.class, data::getAsArray); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + Assertions.assertThrows(ClassCastException.class, data::getAsInteger); + Assertions.assertThrows(ClassCastException.class, data::getAsLong); + Assertions.assertThrows(ClassCastException.class, data::getAsFloat); + Assertions.assertThrows(ClassCastException.class, data::getAsDouble); + } + + @Test + void serializeByteBuffer() { + byte[] value = {(byte) 0xFF}; + PersistedData data = serializer.serialize(ByteBuffer.wrap(value)); + + Assertions.assertEquals(ByteBufferPersistedData.class, data.getClass()); + Assertions.assertTrue(data.isBytes()); + + Assertions.assertArrayEquals(value, data.getAsBytes()); + Assertions.assertEquals(ByteBuffer.wrap(value), data.getAsByteBuffer()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isArray()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isNumber()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + Assertions.assertThrows(IllegalStateException.class, data::getAsArray); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + Assertions.assertThrows(ClassCastException.class, data::getAsInteger); + Assertions.assertThrows(ClassCastException.class, data::getAsLong); + Assertions.assertThrows(ClassCastException.class, data::getAsFloat); + Assertions.assertThrows(ClassCastException.class, data::getAsDouble); + } + + @ParameterizedTest(name = "{1}") + @MethodSource("types") + void serializePersistedDatas(PersistedData entry, Set typeGetters) { + PersistedData data = serializer.serialize(entry); + checkValueArray(data, entry, typeGetters); + } + + @ParameterizedTest(name = "{1}") + @MethodSource("types") + void serializeIterablePersistedData(PersistedData entry, Set typeGetters) { + PersistedData data = serializer.serialize(Collections.singletonList(entry)); + checkValueArray(data, entry, typeGetters); + } + + @Test + void serializeMapStringPersistedData() { + + } + + @Test + void serializeNull() { + PersistedData data = serializer.serializeNull(); + + Assertions.assertTrue(data.isNull()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isArray()); + Assertions.assertFalse(data.isNumber()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + Assertions.assertThrows(IllegalStateException.class, data::getAsArray); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + Assertions.assertThrows(ClassCastException.class, data::getAsInteger); + Assertions.assertThrows(ClassCastException.class, data::getAsLong); + Assertions.assertThrows(ClassCastException.class, data::getAsFloat); + Assertions.assertThrows(ClassCastException.class, data::getAsDouble); + } + + private void checkNumberArray(PersistedData data, Class arrayType, + Class entryType) { + Assertions.assertEquals(arrayType, data.getClass()); + + Assertions.assertTrue(data.isArray()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + + Assertions.assertEquals(1, data.getAsInteger()); + Assertions.assertEquals(1L, data.getAsLong()); + Assertions.assertEquals(1F, data.getAsFloat()); + Assertions.assertEquals(1D, data.getAsDouble()); + + Assertions.assertEquals(entryType, data.getAsArray().getArrayItem(0).getClass()); + + Assertions.assertEquals(1, data.getAsArray().getAsIntegerArray().get(0)); + Assertions.assertEquals(1L, data.getAsArray().getAsLongArray().get(0)); + Assertions.assertEquals(1F, data.getAsArray().getAsFloatArray().get(0)); + Assertions.assertEquals(1D, data.getAsArray().getAsDoubleArray().get(0)); + } + + private void checkIsNumber(PersistedData data) { + Assertions.assertTrue(data.isNumber()); + + Assertions.assertEquals(1, data.getAsInteger()); + Assertions.assertEquals(1L, data.getAsLong()); + Assertions.assertEquals(1F, data.getAsFloat()); + Assertions.assertEquals(1D, data.getAsDouble()); + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isArray()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + Assertions.assertThrows(IllegalStateException.class, data::getAsArray); + + Assertions.assertThrows(DeserializationException.class, data::getAsByteBuffer); + Assertions.assertThrows(DeserializationException.class, data::getAsBytes); + + Assertions.assertThrows(ClassCastException.class, data::getAsString); + Assertions.assertThrows(ClassCastException.class, data::getAsBoolean); + } + + private void checkValueArray(PersistedData data, PersistedData entry, Set typeGetters) { + Assertions.assertEquals(ByteBufferPersistedDataArray.class, data.getClass()); + +// Assertions.assertEquals(entry, data.getAsArray().getArrayItem(0)); + typeGetters.forEach(typeGetter -> { + Object expected = typeGetter.getGetter().apply(entry); + Object actual = typeGetter.getGetter().apply(data); + if (typeGetter == TypeGetter.BYTES) { + Assertions.assertArrayEquals((byte[]) expected, (byte[]) actual); + } else { + Assertions.assertEquals(expected, actual); + } + } + ); + + + Assertions.assertFalse(data.isString()); + Assertions.assertFalse(data.isNull()); + Assertions.assertFalse(data.isNumber()); + Assertions.assertFalse(data.isBoolean()); + Assertions.assertFalse(data.isBytes()); + Assertions.assertFalse(data.isValueMap()); + + Assertions.assertThrows(IllegalStateException.class, data::getAsValueMap); + + Set deserializationExceptionGetters = Sets.newSet( + TypeGetter.BYTE_BUFFER, + TypeGetter.BYTES + ); + deserializationExceptionGetters.stream() + .filter(f -> !typeGetters.contains(f)) + .map(TypeGetter::getGetter) + .map(f -> (Executable) () -> f.apply(data)) + .forEach(e -> + Assertions.assertThrows(DeserializationException.class, e) + ); + + Set classCastExceptionGetters = Sets.newSet( + TypeGetter.BOOLEAN, + TypeGetter.STRING, + TypeGetter.INTEGER, + TypeGetter.LONG, + TypeGetter.FLOAT, + TypeGetter.DOUBLE + ); + classCastExceptionGetters.stream().filter(f -> !typeGetters.contains(f)) + .map(TypeGetter::getGetter) + .map(f -> (Executable) () -> f.apply(data)) + .forEach(e -> + Assertions.assertThrows(ClassCastException.class, e) + ); + } + + private enum TypeGetter { + STRING(PersistedData::getAsString), + BOOLEAN(PersistedData::getAsBoolean), + INTEGER(PersistedData::getAsInteger), + LONG(PersistedData::getAsLong), + FLOAT(PersistedData::getAsFloat), + DOUBLE(PersistedData::getAsDouble), + BYTE_BUFFER(PersistedData::getAsByteBuffer), + BYTES(PersistedData::getAsBytes); + + private final Function getter; + + TypeGetter(Function getter) { + this.getter = getter; + } + + public Function getGetter() { + return getter; + } + } +} diff --git a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java new file mode 100644 index 00000000000..a9cb05d665c --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java @@ -0,0 +1,235 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.collect.Streams; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.reflections.Reflections; +import org.terasology.persistence.serializers.Serializer; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferDataReader; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferDataWriter; +import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferPersistedSerializer; +import org.terasology.persistence.typeHandling.inMemory.InMemoryPersistedDataSerializer; +import org.terasology.persistence.typeHandling.inMemory.InMemoryReader; +import org.terasology.persistence.typeHandling.inMemory.InMemoryWriter; +import org.terasology.reflection.TypeInfo; + +import java.util.EnumSet; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +public class FullSerializationDeserializationTest { + + private static Reflections reflections; + private static TypeHandlerLibrary typeHandlerLibrary; + + @BeforeAll + public static void setup() { + reflections = new Reflections(TypeHandlerLibraryTest.class.getClassLoader()); + typeHandlerLibrary = new TypeHandlerLibrary(reflections); + TypeHandlerLibrary.populateBuiltInHandlers(typeHandlerLibrary); + + } + + private static Stream values() { + return Stream.of( + value(true), + value((byte) 1), +// value((short) 1), + value(1), + value(1L), + value(1F), + value(1D), + value('c'), + value("string"), + value(Locale.ENGLISH) + + ); + } + + private static Stream collections() { + return Stream.of( + Arguments.of(new TypeInfo>() { + }, Lists.newArrayList("String1", "String2")), + Arguments.of(new TypeInfo>() { + }, Sets.newHashSet("String1", "String2")), + Arguments.of(new TypeInfo>() { + }, EnumSet.of(SampleEnum.ONE)), + Arguments.of(new TypeInfo>() { + }, EnumSet.of(SampleEnum.ONE, SampleEnum.THREE)) + ); + } + + private static Stream serializers() { + return Stream.of( + Arguments.of( + new Serializer(typeHandlerLibrary, + new ByteBufferPersistedSerializer(), + new ByteBufferDataWriter(), + new ByteBufferDataReader())), + Arguments.of( + new Serializer(typeHandlerLibrary, + new InMemoryPersistedDataSerializer(), + new InMemoryWriter(), + new InMemoryReader())) + ); + } + + private static Stream product() { + return serializers() + .flatMap(s -> Streams.concat(values(), collections()) + .map(v -> Arguments.of(s.get()[0], v.get()[0], v.get()[1])) + ); + } + + private static Arguments value(Object value) { + return value(value.getClass(), value); + } + + private static Arguments value(Class clazz, Object value) { + return Arguments.of(TypeInfo.of(clazz), value); + } + + + public enum SampleEnum { + ONE, + TWO, + THREE + } + + @MethodSource("product") + @ParameterizedTest(name = "{0} : {1} : {2}") + void test(Serializer serializer, TypeInfo type, T value) { + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertEquals(value, deserialized.get()); + + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testBoolArray(Serializer serializer) { + boolean[] value = new boolean[]{true}; + TypeInfo type = TypeInfo.of(boolean[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testByteArray(Serializer serializer) { + byte[] value = new byte[]{(byte) 1}; + TypeInfo type = TypeInfo.of(byte[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testIntArray(Serializer serializer) { + int[] value = new int[]{1}; + TypeInfo type = TypeInfo.of(int[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testLongArray(Serializer serializer) { + long[] value = new long[]{1L}; + TypeInfo type = TypeInfo.of(long[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testFloatArray(Serializer serializer) { + float[] value = new float[]{1F}; + TypeInfo type = TypeInfo.of(float[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testDoubleArray(Serializer serializer) { + double[] value = new double[]{1D}; + TypeInfo type = TypeInfo.of(double[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testCharArray(Serializer serializer) { + char[] value = new char[]{'c'}; + TypeInfo type = TypeInfo.of(char[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } + + @MethodSource("serializers") + @ParameterizedTest(name = "{0} : {displayName}") + void testObjectArray(Serializer serializer) { + String[] value = new String[]{"String"}; + TypeInfo type = TypeInfo.of(String[].class); + Optional serialized = serializer.serialize(value, type); + Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); + byte[] bytes = serialized.get(); + System.out.println("Size in bytes: " + bytes.length); + Optional deserialized = serializer.deserialize(type, bytes); + Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); + Assertions.assertArrayEquals(value, deserialized.get()); + } +} + From b8416dac808ba46d87695b475908ccdb4b590154 Mon Sep 17 00:00:00 2001 From: darkweird Date: Thu, 28 Oct 2021 16:41:16 +0300 Subject: [PATCH 2/9] feat(ByteBuffer-Serializer): Add Map implementations for ByteBuffer serializers --- .../bytebuffer/ByteBufferPersistedData.java | 6 +- .../ByteBufferPersistedDataMap.java | 102 ++++++++++++++++++ .../ByteBufferPersistedSerializer.java | 37 ++++++- .../FullSerializationDeserializationTest.java | 87 +++++++++++++-- 4 files changed, 221 insertions(+), 11 deletions(-) create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java index ee8559caa69..a33d994da9b 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java @@ -202,7 +202,11 @@ public PersistedDataArray getAsArray() { @Override public PersistedDataMap getAsValueMap() { - throw new IllegalStateException("Not implemeneted yet"); + if (!isValueMap()) { + throw new IllegalStateException("it is not valuemap"); + } + byteBuffer.position(position); + return new ByteBufferPersistedDataMap(byteBuffer); } @Override diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java new file mode 100644 index 00000000000..bfeae07290f --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java @@ -0,0 +1,102 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +import org.terasology.persistence.typeHandling.PersistedData; +import org.terasology.persistence.typeHandling.PersistedDataArray; +import org.terasology.persistence.typeHandling.PersistedDataMap; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class ByteBufferPersistedDataMap extends ByteBufferPersistedData implements PersistedDataMap { + + public ByteBufferPersistedDataMap(ByteBuffer byteBuffer) { + super(byteBuffer); + } + + @Override + public boolean has(String name) { + return index(name) != -1; + } + + @Override + public PersistedData get(String name) { + return new ByteBufferPersistedData(byteBuffer, index(name)); + } + + @Override + public float getAsFloat(String name) { + return get(name).getAsFloat(); + } + + @Override + public int getAsInteger(String name) { + return get(name).getAsInteger(); + } + + @Override + public double getAsDouble(String name) { + return get(name).getAsDouble(); + } + + @Override + public long getAsLong(String name) { + return get(name).getAsLong(); + } + + @Override + public String getAsString(String name) { + return get(name).getAsString(); + } + + @Override + public boolean getAsBoolean(String name) { + return get(name).getAsBoolean(); + } + + @Override + public PersistedDataMap getAsMap(String name) { + return get(name).getAsValueMap(); + } + + @Override + public PersistedDataArray getAsArray(String name) { + return get(name).getAsArray(); + } + + @Override + public Set> entrySet() { + int refPositions = position + 1 + 4; + int size = size(); + Map map = new HashMap<>(size); + for (int i = 0; i < size; i++) { + int keyPos = position + byteBuffer.getInt(refPositions + 8 * i); + int valuePos = position + byteBuffer.getInt(refPositions + 8 * i + 4); + String key = new ByteBufferPersistedData(byteBuffer, keyPos, BBType.STRING.getCode()).getAsString(); + PersistedData value = new ByteBufferPersistedData(byteBuffer, valuePos); + map.put(key, value); + } + return map.entrySet(); + } + + private int size() { + return byteBuffer.getInt(position + 1); + } + + private int index(String name) { + int refPositions = position + 1 + 4; + int size = size(); + for (int i = 0; i < size; i++) { + int keyPos = position + byteBuffer.getInt(refPositions + 8 * i); + String candidateKey = new ByteBufferPersistedData(byteBuffer, keyPos, BBType.STRING.getCode()).getAsString(); + if (name.equals(candidateKey)) { + return position + byteBuffer.getInt(refPositions + 8 * i + 4); + } + } + return -1; + } +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java index 342c974bdd4..6643e9fac5a 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java @@ -18,6 +18,7 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -283,7 +284,41 @@ public PersistedData serialize(Iterable data) { @Override public PersistedData serialize(Map data) { - return null; + int entryCount = data.size(); + int size = 0; + List keys = new ArrayList<>(entryCount); + List values = new ArrayList<>(entryCount); + for (Map.Entry entry : data.entrySet()) { + PersistedData serialize = serialize(entry.getKey()); + ByteBuffer keyBuffer = ((ByteBufferPersistedData) serialize).byteBuffer; + keyBuffer.position(1); // skip header + size += keyBuffer.remaining(); + keys.add(serialize); + size += ((ByteBufferPersistedData) entry.getValue()).byteBuffer.array().length; + values.add(entry.getValue()); + } + int refsSize = 8 * entryCount; + ByteBuffer buffer = ByteBuffer.allocate(1 + 4 + refsSize + size); + buffer.put(BBType.VALUEMAP.getCode()); + buffer.putInt(entryCount); + int dataPosition = 1 + 4 + refsSize; + buffer.position(dataPosition); + for (int i = 0; i < keys.size(); i++) { + ByteBufferPersistedData key = (ByteBufferPersistedData) keys.get(i); + int keySize = key.byteBuffer.remaining(); + buffer.put(key.byteBuffer); + buffer.putInt(1 + 4 + i * 8, dataPosition); + dataPosition += keySize; + } + for (int i = 0; i < values.size(); i++) { + ByteBufferPersistedData value = (ByteBufferPersistedData) values.get(i); + int keySize = value.byteBuffer.array().length; + buffer.put(value.byteBuffer.array()); + buffer.putInt(1 + 4 + i * 8 + 4, dataPosition); + dataPosition += keySize; + } + buffer.rewind(); + return new ByteBufferPersistedDataMap(buffer); } @Override diff --git a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java index a9cb05d665c..8bb74b62298 100644 --- a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java +++ b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java @@ -22,8 +22,11 @@ import org.terasology.reflection.TypeInfo; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; @@ -52,8 +55,16 @@ private static Stream values() { value(1D), value('c'), value("string"), - value(Locale.ENGLISH) - + value(Locale.ENGLISH), + value(new SampleClass( + "className", + -1, + new SampleClass2("child"), + Lists.newArrayList( + new SampleClass2("child1"), + new SampleClass2("child2") + ) + )) ); } @@ -66,7 +77,17 @@ private static Stream collections() { Arguments.of(new TypeInfo>() { }, EnumSet.of(SampleEnum.ONE)), Arguments.of(new TypeInfo>() { - }, EnumSet.of(SampleEnum.ONE, SampleEnum.THREE)) + }, EnumSet.of(SampleEnum.ONE, SampleEnum.THREE)), + Arguments.of(new TypeInfo>() { + }, new HashMap() {{ + put("key1", "value1"); + }}) +// Arguments.arguments(new TypeInfo>>>() { +// }, new HashMap>>() {{ +// put("key1", Lists.newArrayList(new HashMap() {{ +// put("innerKey", SampleEnum.TWO); +// }})); +// }}) Runtime delegate type handler fail it ); } @@ -101,12 +122,6 @@ private static Arguments value(Class clazz, Object value) { } - public enum SampleEnum { - ONE, - TWO, - THREE - } - @MethodSource("product") @ParameterizedTest(name = "{0} : {1} : {2}") void test(Serializer serializer, TypeInfo type, T value) { @@ -231,5 +246,59 @@ void testObjectArray(Serializer serializer) { Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); } + + public enum SampleEnum { + ONE, + TWO, + THREE + } + + public static class SampleClass { + String name; + int value1; + SampleClass2 child; + List children; + + public SampleClass(String name, int value1, SampleClass2 child, List children) { + this.name = name; + this.value1 = value1; + this.child = child; + this.children = children; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SampleClass that = (SampleClass) o; + return value1 == that.value1 && Objects.equals(name, that.name) && Objects.equals(child, that.child) && Objects.equals(children, that.children); + } + + @Override + public int hashCode() { + return Objects.hash(name, value1, child, children); + } + } + + public static class SampleClass2 { + String name; + + public SampleClass2(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SampleClass2 that = (SampleClass2) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + } } From b27bf8138020b7aa21ad0f00ccd359025b7da7fa Mon Sep 17 00:00:00 2001 From: darkweird Date: Thu, 28 Oct 2021 16:43:00 +0300 Subject: [PATCH 3/9] chore(ByteBuffer-Serializer): fix format of autogenerated equals --- .../FullSerializationDeserializationTest.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java index 8bb74b62298..b886d11e795 100644 --- a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java +++ b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java @@ -268,8 +268,12 @@ public SampleClass(String name, int value1, SampleClass2 child, List Date: Fri, 29 Oct 2021 11:33:46 +0300 Subject: [PATCH 4/9] doc(ByteBuffer-Serializer): Add javadocs and format description --- .../typeHandling/bytebuffer/BBType.java | 2 +- .../bytebuffer/ByteBufferPersistedData.java | 19 ++----- .../ByteBufferPersistedDataArray.java | 9 ++++ .../ByteBufferPersistedDataMap.java | 11 ++++ .../ByteBufferPersistedSerializer.java | 2 +- .../typeHandling/bytebuffer/package-info.java | 53 +++++++++++++++++++ 6 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java index fb7a6f5b410..319f5b129b1 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java @@ -4,7 +4,7 @@ package org.terasology.persistence.typeHandling.bytebuffer; /** - * Type codes for types. + * Type codes for array types. */ public enum BBType { NULL(0), diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java index a33d994da9b..81668b2ce6b 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java @@ -12,23 +12,10 @@ import java.nio.charset.StandardCharsets; /** - * ByteBuffer-backed persisted data. + * ByteBuffer-backed persisted data representation. *
- *     Header:
- *     1 byte - header.
- *
- *     Types:
- *      NULL(0) - Null value
- *      BOOLEAN(1) - boolean - not packed. takes 1 byte.
- *      FLOAT(2)
- *      DOUBLE(3)
- *      LONG(4)
- *      INTEGER(5)
- *      STRING(6) - UTF-8. header: 1 int - length
- *      BYTES(7) - header: 1 int - length
- *      BYTEBUFFER(8) - just passthru this bytebuffer //TODO
- *      ARRAY(9) -
- *      VALUEMAP(10) -
+ * 1 byte - BBType
+ * 0..n bytes - data
  * 
*/ public class ByteBufferPersistedData implements PersistedData { diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java index 4754692c7cc..059b8d07b7a 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java @@ -21,6 +21,15 @@ import java.util.List; import java.util.NoSuchElementException; +/** + * ByteBuffer-backed persisted data array representation. + *
+ * 1 byte - BBType
+ * 1 byte - BBArrayType
+ * 4 bytes - size
+ * 0..n bytes - data
+ * 
+ */ public class ByteBufferPersistedDataArray extends ByteBufferPersistedData implements PersistedDataArray { private final BBArrayType arrayType; diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java index bfeae07290f..40b63532a4d 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataMap.java @@ -12,6 +12,17 @@ import java.util.Map; import java.util.Set; +/** + * ByteBuffer-backed persisted data map representation. + *
+ *     1 byte -  type = 10
+ *     4 byte - size
+ *     8 * size bytes - refmap
+ *     0..n bytes - key and value data.
+ * 
+ *

+ * Use refmap - links to key and value positions. provide almost constant read time. + */ public class ByteBufferPersistedDataMap extends ByteBufferPersistedData implements PersistedDataMap { public ByteBufferPersistedDataMap(ByteBuffer byteBuffer) { diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java index 6643e9fac5a..e229cdf407b 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java @@ -25,7 +25,7 @@ /** - * TODO make direct serializer factory. + * Provide possible serialize data to ByteBuffer. */ public class ByteBufferPersistedSerializer implements PersistedDataSerializer { diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java new file mode 100644 index 00000000000..56387d43807 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java @@ -0,0 +1,53 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +/** + * ByteBuffer serializer provide possible to read and write bytebuffer directly. + *

+ *

+ *         Format:
+ *         Types(BBType):
+ *            NULL(0) - size = 1 byte, format = type(byte value = 0)
+ *            BOOLEAN(1) - size = 2 bytes, format = type(byte value = 1) + value(1 byte). ( yeah a bit not optimal)
+ *            FLOAT(2) - size = 5 bytes, format = type(byte value = 2) + value(1 float = 4 bytes)
+ *            DOUBLE(3) - size = 9 bytes, format = type(byte value = 3) + value(1 double = 8 bytes)
+ *            LONG(4) -  size = 9 bytes, format = type(byte value = 4) + value(1 long = 8 bytes)
+ *            INTEGER(5) - size = 5 bytes, format = type(byte value = 5) + value(1 int = 4 bytes)
+ *            STRING(6) - size = 5..n bytes, format =  type(byte value = 6) + size(1 int = 4 bytes) + value($size bytes)
+ *            BYTES(7) - size = 5..n bytes, format =  type(byte value = 7) + size(1 int = 4 bytes) + value($size bytes)
+ *            BYTEBUFFER(8) - size = 5..n bytes, format =  type(byte value = 8) + size(1 int = 4 bytes) + value($size bytes)
+ *            ARRAY(9) -
+ *                  size = 6..n bytes.
+ *                  format = type(byte value = 9) + data(see ArrayTypes)
+ *            VALUEMAP(10) -
+ *                  size = 5..n bytes.
+ *                  format =  type(byte value = 10) +
+ *                            size(1 int = 4 bytes) +
+ *                            refmap(format = (keyRef(1 int) + valueRef(1 int)) * $size) +
+ *                            keydata(format = (STRING(6) - 1 byte($STRING.type) * $size ) +
+ *                            valuedata(format = (any BBType) * $size)
+ *
+ *         ArrayTypes(BBArrayType):
+ *            BOOLEAN(0) - size = 5..n bytes.
+ *                       format = arraytype(byte value = 0) + size(1 int = 4 bytes) + data(($size % 8 + 1) bytes)
+ *            FLOAT(1) - size = 5 .. n bytes.
+ *                       format = arraytype(byte value = 1) + size(1 int = 4 bytes) + data($size floats = $size * 4 bytes)
+ *            DOUBLE(2) - size = 5 .. n bytes.
+ *                       format = arraytype(byte value = 2) + size(1 int = 4 bytes) + data($size double = $size * 8 bytes)
+ *            LONG(3) - size = 5 .. n bytes.
+ *                       format = arraytype(byte value = 3) + size(1 int = 4 bytes) + data($size longs = $size * 8 bytes)
+ *            INTEGER(4) - size = 5 .. n bytes.
+ *                       format = arraytype(byte value = 4) + size(1 int = 4 bytes) + data($size ints = $size * 4 bytes)
+ *            STRING(5) - size = 5 .. n bytes.
+ *                       format = arraytype(byte value = 5) +
+ *                                size(1 int = 4 bytes) +
+ *                                sizeArray(format = STRING(6).size = 1 int * $size = 4 byte * $size) +
+ *                                stringdata(format = (STRING(6) - 1 byte($STRING.type)) * $size)
+ *            VALUE(6) - size = 5 .. n bytes.
+ *                       format = arraytype(byte value = 5) +
+ *                                size(1 int = 4 bytes) +
+ *                                sizeArray(format = (any BBType whole size) = 1 int * $size = 4 byte * $size) +
+ *                                stringdata(format = (any BBType) * $size)
+ *     
+ */ +package org.terasology.persistence.typeHandling.bytebuffer; From 7c8347b258d6876009f496029f045b44848235da Mon Sep 17 00:00:00 2001 From: darkweird Date: Mon, 29 Nov 2021 11:45:33 +0300 Subject: [PATCH 5/9] chore(ByteBuffer-Serializer): use more exact exception description --- .../bytebuffer/ByteBufferPersistedData.java | 24 +++++++++---------- .../ByteBufferPersistedDataArray.java | 16 ++++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java index 81668b2ce6b..56fa295f761 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java @@ -58,7 +58,7 @@ public ByteBuffer getData() { @Override public String getAsString() { if (!isString()) { - throw new ClassCastException("it is not string"); + throw new ClassCastException(String.format("Source is not of type string: %s", type.name())); } resetPosition(); int size = byteBuffer.getInt(); @@ -74,7 +74,7 @@ private void resetPosition() { @Override public double getAsDouble() { if (!isNumber()) { - throw new ClassCastException("It is not number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } resetPosition(); switch (type) { @@ -93,7 +93,7 @@ public double getAsDouble() { @Override public float getAsFloat() { if (!isNumber()) { - throw new ClassCastException("It is not number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } resetPosition(); switch (type) { @@ -106,13 +106,13 @@ public float getAsFloat() { case LONG: return (float) getData().getLong(); } - throw new ClassCastException("Data is not a number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } @Override public int getAsInteger() { if (!isNumber()) { - throw new ClassCastException("It is not number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } resetPosition(); switch (type) { @@ -131,7 +131,7 @@ public int getAsInteger() { @Override public long getAsLong() { if (!isNumber()) { - throw new ClassCastException("It is not number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } resetPosition(); switch (type) { @@ -144,14 +144,14 @@ public long getAsLong() { case LONG: return getData().getLong(); } - throw new ClassCastException("Data is not a number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } @Override public boolean getAsBoolean() { if (!isBoolean()) { - throw new ClassCastException("It is not boolean"); + throw new ClassCastException(String.format("Source is not of type boolean: %s", type.name())); } resetPosition(); return byteBuffer.get() != 0; // Don't Packed booleans @@ -160,7 +160,7 @@ public boolean getAsBoolean() { @Override public byte[] getAsBytes() { if (!isBytes()) { - throw new DeserializationException("it is not bytes or bytebuffer"); + throw new DeserializationException(String.format("Source is not of type bytes or bytebuffer: %s", type.name())); } resetPosition(); int size = byteBuffer.getInt(); @@ -172,7 +172,7 @@ public byte[] getAsBytes() { @Override public ByteBuffer getAsByteBuffer() { if (!isBytes()) { - throw new DeserializationException("it is not bytes or bytebuffer"); + throw new DeserializationException(String.format("Source is not of type bytes or bytebuffer: %s", type.name())); } resetPosition(); return ByteBuffer.wrap(getAsBytes()); @@ -181,7 +181,7 @@ public ByteBuffer getAsByteBuffer() { @Override public PersistedDataArray getAsArray() { if (!isArray()) { - throw new IllegalStateException("it is not array"); + throw new IllegalStateException(String.format("Source is not of type array: %s", type.name())); } byteBuffer.position(position); return new ByteBufferPersistedDataArray(byteBuffer); @@ -190,7 +190,7 @@ public PersistedDataArray getAsArray() { @Override public PersistedDataMap getAsValueMap() { if (!isValueMap()) { - throw new IllegalStateException("it is not valuemap"); + throw new IllegalStateException(String.format("Source is not of type valuemap: %s", type.name())); } byteBuffer.position(position); return new ByteBufferPersistedDataMap(byteBuffer); diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java index 059b8d07b7a..0c97c57cbad 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java @@ -98,7 +98,7 @@ public String getAsString() { return data.getAsString(); } } - throw new ClassCastException("it is not string array"); + throw new ClassCastException(String.format("Source is not of type string array: %s", arrayType.name())); } } @@ -118,7 +118,7 @@ public double getAsDouble() { return data.getAsDouble(); } } - throw new ClassCastException("it is not number array"); + throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); } } @@ -137,7 +137,7 @@ public float getAsFloat() { return data.getAsFloat(); } } - throw new ClassCastException("it is not number array"); + throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); } } @@ -156,7 +156,7 @@ public int getAsInteger() { return data.getAsInteger(); } } - throw new ClassCastException("it is not number array"); + throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); } } @@ -175,7 +175,7 @@ public long getAsLong() { return data.getAsLong(); } } - throw new ClassCastException("it is not number array"); + throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); } } @@ -339,7 +339,7 @@ public boolean getAsBoolean() { return data.getAsBoolean(); } } - throw new ClassCastException("it is not boolean array"); + throw new ClassCastException(String.format("Source is not of type boolean array: %s", arrayType.name())); } } @@ -351,7 +351,7 @@ public byte[] getAsBytes() { return data.getAsBytes(); } } - throw new DeserializationException("it is not bytes array"); + throw new DeserializationException(String.format("Source is not of type bytes array: %s", arrayType.name())); } @Override @@ -362,6 +362,6 @@ public ByteBuffer getAsByteBuffer() { return data.getAsByteBuffer(); } } - throw new DeserializationException("it is not bytes array"); + throw new DeserializationException(String.format("Source is not of type bytes array: %s", arrayType.name())); } } From 546919abb804b6be120b305e8ae5212dd8103764 Mon Sep 17 00:00:00 2001 From: darkweird Date: Mon, 29 Nov 2021 13:35:10 +0300 Subject: [PATCH 6/9] refactor(ByteBuffer-Serializer): merge BBType and BBArrayType --- .../typeHandling/bytebuffer/BBArrayType.java | 54 ------------ .../typeHandling/bytebuffer/BBConsts.java | 18 ++++ .../typeHandling/bytebuffer/BBType.java | 52 ++++++++++-- .../bytebuffer/ByteBufferPersistedData.java | 4 +- .../ByteBufferPersistedDataArray.java | 85 +++++++++---------- .../ByteBufferPersistedSerializer.java | 59 ++++++------- .../typeHandling/bytebuffer/package-info.java | 48 +++++------ 7 files changed, 156 insertions(+), 164 deletions(-) delete mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java create mode 100644 subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java deleted file mode 100644 index 2dd77883b2d..00000000000 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBArrayType.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.persistence.typeHandling.bytebuffer; - -/** - * Type codes for types. - */ -public enum BBArrayType { - BOOLEAN(0), - FLOAT(1), - DOUBLE(2), - LONG(3), - INTEGER(4), - STRING(5), - VALUE(6); - - private final byte code; - - BBArrayType(int code) { - this.code = (byte) code; - } - - public byte getCode() { - return code; - } - - public BBType getPrimitiveType() { - switch (this) { - case BOOLEAN: - return BBType.BOOLEAN; - case FLOAT: - return BBType.FLOAT; - case DOUBLE: - return BBType.DOUBLE; - case LONG: - return BBType.LONG; - case INTEGER: - return BBType.INTEGER; - case STRING: - return BBType.STRING; - } - return null; - } - - public static BBArrayType parse(byte code) { - for (BBArrayType type : values()) { - if (type.code == code) { - return type; - } - } - return null; - } -} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java new file mode 100644 index 00000000000..29ac269d6d1 --- /dev/null +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java @@ -0,0 +1,18 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.persistence.typeHandling.bytebuffer; + +public class BBConsts { + + public static final int DOUBLE_SIZE = 8; + public static final int BYTE_SIZE = 1; + public static final int SHORT_SIZE = 2; + public static final int INT_SIZE = 4; + public static final int FLOAT_SIZE = 4; + public static final int LONG_SIZE = 8; + + + public static final int TYPE_HEADER = 1; + public static final int SIZE_HEADER = INT_SIZE; +} diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java index 319f5b129b1..d6d1fb3a5a6 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBType.java @@ -16,8 +16,14 @@ public enum BBType { STRING(6), BYTES(7), BYTEBUFFER(8), - ARRAY(9), - VALUEMAP(10); + BOOLEAN_ARRAY(9), + FLOAT_ARRAY(10), + DOUBLE_ARRAY(11), + LONG_ARRAY(12), + INTEGER_ARRAY(13), + STRING_ARRAY(14), + VALUE_ARRAY(15), + VALUEMAP(16); private final byte code; @@ -25,10 +31,6 @@ public enum BBType { this.code = (byte) code; } - public byte getCode() { - return code; - } - public static BBType parse(byte code) { for (BBType type : values()) { if (type.code == code) { @@ -37,4 +39,42 @@ public static BBType parse(byte code) { } return null; } + + + public BBType getPrimitiveType() { + switch (this) { + case BOOLEAN_ARRAY: + return BBType.BOOLEAN; + case FLOAT_ARRAY: + return BBType.FLOAT; + case DOUBLE_ARRAY: + return BBType.DOUBLE; + case LONG_ARRAY: + return BBType.LONG; + case INTEGER_ARRAY: + return BBType.INTEGER; + case STRING_ARRAY: + return BBType.STRING; + } + return null; + } + + public boolean isArray() { + switch (this) { + case BOOLEAN_ARRAY: + case FLOAT_ARRAY: + case DOUBLE_ARRAY: + case LONG_ARRAY: + case INTEGER_ARRAY: + case STRING_ARRAY: + case VALUE_ARRAY: + return true; + default: + return false; + } + } + + public byte getCode() { + return code; + } } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java index 56fa295f761..737ca7ae623 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java @@ -21,7 +21,7 @@ public class ByteBufferPersistedData implements PersistedData { protected final ByteBuffer byteBuffer; protected final int position; - private final BBType type; + protected final BBType type; private final boolean typeForced; public ByteBufferPersistedData(ByteBuffer byteBuffer, int position, byte type) { @@ -221,7 +221,7 @@ public boolean isBytes() { @Override public boolean isArray() { - return type == BBType.ARRAY; + return type.isArray(); } @Override diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java index 0c97c57cbad..400e2046e8b 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java @@ -25,19 +25,16 @@ * ByteBuffer-backed persisted data array representation. *
  * 1 byte - BBType
- * 1 byte - BBArrayType
  * 4 bytes - size
  * 0..n bytes - data
  * 
*/ public class ByteBufferPersistedDataArray extends ByteBufferPersistedData implements PersistedDataArray { - private final BBArrayType arrayType; private final int size; public ByteBufferPersistedDataArray(ByteBuffer byteBuffer) { super(byteBuffer); - arrayType = BBArrayType.parse(byteBuffer.get()); size = byteBuffer.getInt(); } @@ -48,7 +45,7 @@ public int size() { @Override public PersistedData getArrayItem(int index) { - BBType primitiveType = arrayType.getPrimitiveType(); + BBType primitiveType = type.getPrimitiveType(); if (primitiveType != null) { return new ByteBufferPersistedData(byteBuffer, calculateIndex(index), primitiveType.getCode()); } else { @@ -58,20 +55,20 @@ public PersistedData getArrayItem(int index) { @Override public boolean isNumberArray() { - return arrayType == BBArrayType.FLOAT - || arrayType == BBArrayType.DOUBLE - || arrayType == BBArrayType.INTEGER - || arrayType == BBArrayType.LONG; + return type == BBType.FLOAT_ARRAY + || type == BBType.DOUBLE_ARRAY + || type == BBType.INTEGER_ARRAY + || type == BBType.LONG_ARRAY; } @Override public boolean isBooleanArray() { - return arrayType == BBArrayType.BOOLEAN; + return type == BBType.BOOLEAN_ARRAY; } @Override public boolean isStringArray() { - return arrayType == BBArrayType.STRING; + return type == BBType.STRING_ARRAY; } @Override @@ -98,7 +95,7 @@ public String getAsString() { return data.getAsString(); } } - throw new ClassCastException(String.format("Source is not of type string array: %s", arrayType.name())); + throw new ClassCastException(String.format("Source is not of type string array: %s", type.name())); } } @@ -118,7 +115,7 @@ public double getAsDouble() { return data.getAsDouble(); } } - throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); + throw new ClassCastException(String.format("Source is not of type number array: %s", type.name())); } } @@ -137,7 +134,7 @@ public float getAsFloat() { return data.getAsFloat(); } } - throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); + throw new ClassCastException(String.format("Source is not of type number array: %s", type.name())); } } @@ -156,7 +153,7 @@ public int getAsInteger() { return data.getAsInteger(); } } - throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); + throw new ClassCastException(String.format("Source is not of type number array: %s", type.name())); } } @@ -175,15 +172,15 @@ public long getAsLong() { return data.getAsLong(); } } - throw new ClassCastException(String.format("Source is not of type number array: %s", arrayType.name())); + throw new ClassCastException(String.format("Source is not of type number array: %s", type.name())); } } @Override public TDoubleList getAsDoubleArray() { - byteBuffer.position(position + 6); + byteBuffer.position(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER); TDoubleList list = new TDoubleArrayList(size()); - Iterator iter = typedIterator(arrayType.getPrimitiveType()); + Iterator iter = typedIterator(type.getPrimitiveType()); while (iter.hasNext()) { list.add(iter.next().getAsDouble()); } @@ -192,9 +189,9 @@ public TDoubleList getAsDoubleArray() { @Override public TFloatList getAsFloatArray() { - byteBuffer.position(position + 6); + byteBuffer.position(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER); TFloatList list = new TFloatArrayList(size()); - Iterator iter = typedIterator(arrayType.getPrimitiveType()); + Iterator iter = typedIterator(type.getPrimitiveType()); while (iter.hasNext()) { list.add(iter.next().getAsFloat()); } @@ -203,9 +200,9 @@ public TFloatList getAsFloatArray() { @Override public TIntList getAsIntegerArray() { - byteBuffer.position(position + 6); + byteBuffer.position(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER); TIntList list = new TIntArrayList(size()); - Iterator iter = typedIterator(arrayType.getPrimitiveType()); + Iterator iter = typedIterator(type.getPrimitiveType()); while (iter.hasNext()) { list.add(iter.next().getAsInteger()); } @@ -214,9 +211,9 @@ public TIntList getAsIntegerArray() { @Override public TLongList getAsLongArray() { - byteBuffer.position(position + 6); + byteBuffer.position(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER); TLongList list = new TLongArrayList(size()); - Iterator iter = typedIterator(arrayType.getPrimitiveType()); + Iterator iter = typedIterator(type.getPrimitiveType()); while (iter.hasNext()) { list.add(iter.next().getAsLong()); } @@ -225,7 +222,7 @@ public TLongList getAsLongArray() { @Override public boolean[] getAsBooleanArray() { - byteBuffer.position(position + 6); + byteBuffer.position(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER); int sizeInBytes = size() % 8 + 1; byte[] bytes = new byte[sizeInBytes]; byteBuffer.get(bytes); @@ -243,7 +240,7 @@ public boolean[] getAsBooleanArray() { @Override public List getAsValueArray() { - byteBuffer.position(position + 6); + byteBuffer.position(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER); List data = new ArrayList<>(size()); for (int i = 0; i < size(); i++) { data.add(new ByteBufferPersistedData(byteBuffer, calculateIndex(i))); @@ -295,29 +292,29 @@ public PersistedData next() { } private int calculateIndex(int index) { - switch (arrayType) { - case BOOLEAN: - return 6 + index % 8 + 1; - case FLOAT: - case INTEGER: - return 6 + index * 4; - case DOUBLE: - case LONG: - return 6 + index * 8; - case STRING: { - int pos = position + 6; + switch (type) { + case BOOLEAN_ARRAY: + return BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + index % 8 + 1; + case FLOAT_ARRAY: + case INTEGER_ARRAY: + return BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + index * 4; + case DOUBLE_ARRAY: + case LONG_ARRAY: + return BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + index * 8; + case STRING_ARRAY: { + int pos = position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER; for (int i = 0; i < index; i++) { - pos += byteBuffer.getInt(pos) + 4; + pos += byteBuffer.getInt(pos) + BBConsts.SIZE_HEADER; } return pos; } - case VALUE: { + case VALUE_ARRAY: { int pos = 0; for (int i = 0; i < index; i++) { - pos += byteBuffer.getInt(position + 6 + i * 4); + pos += byteBuffer.getInt(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + i * BBConsts.SIZE_HEADER); } int sizeArraySize = size() * 4; - return pos + position + 6 + sizeArraySize; + return pos + position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + sizeArraySize; } } @@ -328,7 +325,7 @@ private int calculateIndex(int index) { public boolean getAsBoolean() { if (isBooleanArray()) { if (size() == 1) { - return (byteBuffer.get(position + 6) & 1) == 1; + return (byteBuffer.get(position + BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER) & 1) == 1; } else { throw new IllegalStateException("boolean array have size != 1"); } @@ -339,7 +336,7 @@ public boolean getAsBoolean() { return data.getAsBoolean(); } } - throw new ClassCastException(String.format("Source is not of type boolean array: %s", arrayType.name())); + throw new ClassCastException(String.format("Source is not of type boolean array: %s", type.name())); } } @@ -351,7 +348,7 @@ public byte[] getAsBytes() { return data.getAsBytes(); } } - throw new DeserializationException(String.format("Source is not of type bytes array: %s", arrayType.name())); + throw new DeserializationException(String.format("Source is not of type bytes array: %s", type.name())); } @Override @@ -362,6 +359,6 @@ public ByteBuffer getAsByteBuffer() { return data.getAsByteBuffer(); } } - throw new DeserializationException(String.format("Source is not of type bytes array: %s", arrayType.name())); + throw new DeserializationException(String.format("Source is not of type bytes array: %s", type.name())); } } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java index e229cdf407b..3b0b6f7682d 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedSerializer.java @@ -32,7 +32,7 @@ public class ByteBufferPersistedSerializer implements PersistedDataSerializer { @Override public PersistedData serialize(String value) { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - ByteBuffer buffer = ByteBuffer.allocate(bytes.length + 1 + 4); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + bytes.length); buffer.put(BBType.STRING.getCode()); buffer.putInt(bytes.length); buffer.put(bytes); @@ -59,9 +59,8 @@ public PersistedData serializeStrings(Iterable value) { if (size == 1) { return buffers.get(0); } - ByteBuffer buffer = ByteBuffer.allocate(byteSize + 4); - buffer.put(BBType.ARRAY.getCode()); - buffer.put(BBArrayType.STRING.getCode()); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.SIZE_HEADER + byteSize); + buffer.put(BBType.STRING_ARRAY.getCode()); buffer.putInt(size); for (ByteBufferPersistedData data : buffers) { data.getData().position(1); // remove header of string. @@ -73,7 +72,7 @@ public PersistedData serializeStrings(Iterable value) { @Override public PersistedData serialize(float value) { - ByteBuffer buffer = ByteBuffer.allocate(5); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.FLOAT_SIZE); buffer.put(BBType.FLOAT.getCode()); buffer.putFloat(value); buffer.rewind(); @@ -82,9 +81,8 @@ public PersistedData serialize(float value) { @Override public PersistedData serialize(float... values) { - ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 4); - buffer.put(BBType.ARRAY.getCode()); - buffer.put(BBArrayType.FLOAT.getCode()); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + values.length * BBConsts.FLOAT_SIZE); + buffer.put(BBType.FLOAT_ARRAY.getCode()); buffer.putInt(values.length); for (float value : values) { buffer.putFloat(value); @@ -104,7 +102,7 @@ public PersistedData serialize(TFloatIterator value) { @Override public PersistedData serialize(int value) { - ByteBuffer buffer = ByteBuffer.allocate(5); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.INT_SIZE); buffer.put(BBType.INTEGER.getCode()); buffer.putInt(value); buffer.rewind(); @@ -113,9 +111,8 @@ public PersistedData serialize(int value) { @Override public PersistedData serialize(int... values) { - ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 4); - buffer.put(BBType.ARRAY.getCode()); - buffer.put(BBArrayType.INTEGER.getCode()); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + values.length * BBConsts.INT_SIZE); + buffer.put(BBType.INTEGER_ARRAY.getCode()); buffer.putInt(values.length); for (int value : values) { buffer.putInt(value); @@ -135,7 +132,7 @@ public PersistedData serialize(TIntIterator value) { @Override public PersistedData serialize(long value) { - ByteBuffer buffer = ByteBuffer.allocate(9); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.LONG_SIZE); buffer.put(BBType.LONG.getCode()); buffer.putLong(value); buffer.rewind(); @@ -144,9 +141,8 @@ public PersistedData serialize(long value) { @Override public PersistedData serialize(long... values) { - ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 8); - buffer.put(BBType.ARRAY.getCode()); - buffer.put(BBArrayType.LONG.getCode()); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + values.length * BBConsts.LONG_SIZE); + buffer.put(BBType.LONG_ARRAY.getCode()); buffer.putInt(values.length); for (long value : values) { buffer.putLong(value); @@ -166,7 +162,7 @@ public PersistedData serialize(TLongIterator value) { @Override public PersistedData serialize(boolean value) { - ByteBuffer buffer = ByteBuffer.allocate(9); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.BYTE_SIZE); buffer.put(BBType.BOOLEAN.getCode()); buffer.put(value ? (byte) 1 : (byte) 0); buffer.rewind(); @@ -177,9 +173,8 @@ public PersistedData serialize(boolean value) { public PersistedData serialize(boolean... values) { int size = values.length; int sizeInBytes = size % 8 + 1; - ByteBuffer buffer = ByteBuffer.allocate(6 + sizeInBytes); - buffer.put(BBType.ARRAY.getCode()); - buffer.put(BBArrayType.BOOLEAN.getCode()); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + sizeInBytes); + buffer.put(BBType.BOOLEAN_ARRAY.getCode()); buffer.putInt(size); for (int i = 0; i < sizeInBytes; i++) { byte valueByte = 0; @@ -199,7 +194,7 @@ public PersistedData serialize(boolean... values) { @Override public PersistedData serialize(double value) { - ByteBuffer buffer = ByteBuffer.allocate(10); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.DOUBLE_SIZE); buffer.put(BBType.DOUBLE.getCode()); buffer.putDouble(value); buffer.rewind(); @@ -208,9 +203,8 @@ public PersistedData serialize(double value) { @Override public PersistedData serialize(double... values) { - ByteBuffer buffer = ByteBuffer.allocate(2 + 4 + values.length * 8); - buffer.put(BBType.ARRAY.getCode()); - buffer.put(BBArrayType.DOUBLE.getCode()); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + values.length * BBConsts.DOUBLE_SIZE); + buffer.put(BBType.DOUBLE_ARRAY.getCode()); buffer.putInt(values.length); for (double value : values) { buffer.putDouble(value); @@ -230,7 +224,7 @@ public PersistedData serialize(TDoubleIterator value) { @Override public PersistedData serialize(byte[] value) { - ByteBuffer buffer = ByteBuffer.allocate(value.length + 1 + 4); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + value.length); buffer.put(BBType.BYTES.getCode()); buffer.putInt(value.length); buffer.put(value); @@ -241,7 +235,7 @@ public PersistedData serialize(byte[] value) { @Override public PersistedData serialize(ByteBuffer value) { int size = value.array().length; - ByteBuffer buffer = ByteBuffer.allocate(1 + 4 + size); + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + size); buffer.put(BBType.BYTEBUFFER.getCode()); buffer.putInt(size); buffer.put(value); @@ -260,9 +254,9 @@ public PersistedData serialize(PersistedData... values) { bytes += length; sizes[i] = length; } - ByteBuffer buffer = ByteBuffer.allocate(6 + values.length * 4 + bytes); - buffer.put(BBType.ARRAY.getCode()); - buffer.put(BBArrayType.VALUE.getCode()); + int refSize = BBConsts.INT_SIZE; + ByteBuffer buffer = ByteBuffer.allocate(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + values.length * refSize + bytes); + buffer.put(BBType.VALUE_ARRAY.getCode()); buffer.putInt(values.length); // Write data sizes for (int size : sizes) { @@ -297,7 +291,8 @@ public PersistedData serialize(Map data) { size += ((ByteBufferPersistedData) entry.getValue()).byteBuffer.array().length; values.add(entry.getValue()); } - int refsSize = 8 * entryCount; + int oneRefSize = BBConsts.INT_SIZE; + int refsSize = oneRefSize * 2 * entryCount; ByteBuffer buffer = ByteBuffer.allocate(1 + 4 + refsSize + size); buffer.put(BBType.VALUEMAP.getCode()); buffer.putInt(entryCount); @@ -307,14 +302,14 @@ public PersistedData serialize(Map data) { ByteBufferPersistedData key = (ByteBufferPersistedData) keys.get(i); int keySize = key.byteBuffer.remaining(); buffer.put(key.byteBuffer); - buffer.putInt(1 + 4 + i * 8, dataPosition); + buffer.putInt(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + i * oneRefSize * 2, dataPosition); dataPosition += keySize; } for (int i = 0; i < values.size(); i++) { ByteBufferPersistedData value = (ByteBufferPersistedData) values.get(i); int keySize = value.byteBuffer.array().length; buffer.put(value.byteBuffer.array()); - buffer.putInt(1 + 4 + i * 8 + 4, dataPosition); + buffer.putInt(BBConsts.TYPE_HEADER + BBConsts.SIZE_HEADER + i * oneRefSize * 2 + oneRefSize, dataPosition); dataPosition += keySize; } buffer.rewind(); diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java index 56387d43807..9b0a0750405 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/package-info.java @@ -16,38 +16,34 @@ * STRING(6) - size = 5..n bytes, format = type(byte value = 6) + size(1 int = 4 bytes) + value($size bytes) * BYTES(7) - size = 5..n bytes, format = type(byte value = 7) + size(1 int = 4 bytes) + value($size bytes) * BYTEBUFFER(8) - size = 5..n bytes, format = type(byte value = 8) + size(1 int = 4 bytes) + value($size bytes) - * ARRAY(9) - - * size = 6..n bytes. - * format = type(byte value = 9) + data(see ArrayTypes) - * VALUEMAP(10) - - * size = 5..n bytes. - * format = type(byte value = 10) + - * size(1 int = 4 bytes) + - * refmap(format = (keyRef(1 int) + valueRef(1 int)) * $size) + - * keydata(format = (STRING(6) - 1 byte($STRING.type) * $size ) + - * valuedata(format = (any BBType) * $size) - * - * ArrayTypes(BBArrayType): - * BOOLEAN(0) - size = 5..n bytes. - * format = arraytype(byte value = 0) + size(1 int = 4 bytes) + data(($size % 8 + 1) bytes) - * FLOAT(1) - size = 5 .. n bytes. - * format = arraytype(byte value = 1) + size(1 int = 4 bytes) + data($size floats = $size * 4 bytes) - * DOUBLE(2) - size = 5 .. n bytes. - * format = arraytype(byte value = 2) + size(1 int = 4 bytes) + data($size double = $size * 8 bytes) - * LONG(3) - size = 5 .. n bytes. - * format = arraytype(byte value = 3) + size(1 int = 4 bytes) + data($size longs = $size * 8 bytes) - * INTEGER(4) - size = 5 .. n bytes. - * format = arraytype(byte value = 4) + size(1 int = 4 bytes) + data($size ints = $size * 4 bytes) - * STRING(5) - size = 5 .. n bytes. - * format = arraytype(byte value = 5) + + * BOOLEAN(9) - size = 5..n bytes. + * format = type(byte value = 9) + size(1 int = 4 bytes) + data(($size % 8 + 1) bytes) + * FLOAT(10) - size = 5 .. n bytes. + * format = type(byte value = 10) + size(1 int = 4 bytes) + data($size floats = $size * 4 bytes) + * DOUBLE(11) - size = 5 .. n bytes. + * format = type(byte value = 11) + size(1 int = 4 bytes) + data($size double = $size * 8 bytes) + * LONG(12) - size = 5 .. n bytes. + * format = type(byte value = 12) + size(1 int = 4 bytes) + data($size longs = $size * 8 bytes) + * INTEGER(13) - size = 5 .. n bytes. + * format = type(byte value = 13) + size(1 int = 4 bytes) + data($size ints = $size * 4 bytes) + * STRING(14) - size = 5 .. n bytes. + * format = type(byte value = 14) + * size(1 int = 4 bytes) + * sizeArray(format = STRING(6).size = 1 int * $size = 4 byte * $size) + * stringdata(format = (STRING(6) - 1 byte($STRING.type)) * $size) - * VALUE(6) - size = 5 .. n bytes. - * format = arraytype(byte value = 5) + + * VALUE(15) - size = 5 .. n bytes. + * format = type(byte value = 15) + * size(1 int = 4 bytes) + * sizeArray(format = (any BBType whole size) = 1 int * $size = 4 byte * $size) + * stringdata(format = (any BBType) * $size) + * VALUEMAP(16) - + * size = 5..n bytes. + * format = type(byte value = 16) + + * size(1 int = 4 bytes) + + * refmap(format = (keyRef(1 int) + valueRef(1 int)) * $size) + + * keydata(format = (STRING(6) - 1 byte($STRING.type) * $size ) + + * valuedata(format = (any BBType) * $size) + * * */ package org.terasology.persistence.typeHandling.bytebuffer; From b743f45920eb3932b886cc23bb2da1ee3b994281 Mon Sep 17 00:00:00 2001 From: darkweird Date: Mon, 29 Nov 2021 13:38:12 +0300 Subject: [PATCH 7/9] chore(ByteBuffer-Serializer): JavaDocs for BBConsts and fix checkstyle issues --- .../typeHandling/bytebuffer/BBConsts.java | 12 ++++++++---- .../bytebuffer/ByteBufferPersistedDataArray.java | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java index 29ac269d6d1..898271551fc 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/BBConsts.java @@ -3,16 +3,20 @@ package org.terasology.persistence.typeHandling.bytebuffer; -public class BBConsts { - +/** + * ByteBuffer serializer constants. + */ +public final class BBConsts { public static final int DOUBLE_SIZE = 8; public static final int BYTE_SIZE = 1; - public static final int SHORT_SIZE = 2; public static final int INT_SIZE = 4; public static final int FLOAT_SIZE = 4; public static final int LONG_SIZE = 8; - public static final int TYPE_HEADER = 1; public static final int SIZE_HEADER = INT_SIZE; + private BBConsts() { + + } + } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java index 400e2046e8b..e34cac48c61 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedDataArray.java @@ -250,7 +250,7 @@ public List getAsValueArray() { @Override public Iterator iterator() { - return new Iterator() { + return new Iterator<>() { private int index; @Override @@ -271,7 +271,7 @@ public PersistedData next() { } private Iterator typedIterator(BBType type) { - return new Iterator() { + return new Iterator<>() { private int index; @Override From bb5bbc56abfd78764a8cc6ab5fab25781f700fa0 Mon Sep 17 00:00:00 2001 From: darkweird Date: Mon, 29 Nov 2021 14:24:15 +0300 Subject: [PATCH 8/9] feat(ByteBuffer-Serializer): implement handling error cases in InmemorySerialization. --- .../typeHandling/bytebuffer/ByteBufferPersistedData.java | 4 ++-- .../inMemory/InMemoryPersistedDataSerializer.java | 2 +- .../persistence/typeHandling/inMemory/InMemoryReader.java | 7 ++++++- .../persistence/typeHandling/inMemory/InMemoryWriter.java | 8 ++++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java index 737ca7ae623..3150f204941 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/bytebuffer/ByteBufferPersistedData.java @@ -87,7 +87,7 @@ public double getAsDouble() { case LONG: return (double) getData().getLong(); } - throw new ClassCastException("Data is not a number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } @Override @@ -125,7 +125,7 @@ public int getAsInteger() { case LONG: return (int) getData().getLong(); } - throw new ClassCastException("Data is not a number"); + throw new ClassCastException(String.format("Source is not of type number: %s", type.name())); } @Override diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryPersistedDataSerializer.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryPersistedDataSerializer.java index be640bbb779..8a2e650948c 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryPersistedDataSerializer.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryPersistedDataSerializer.java @@ -28,7 +28,7 @@ public class InMemoryPersistedDataSerializer implements PersistedDataSerializer { - public static final PersistedData NULL = new AbstractPersistedData() { + public static final AbstractPersistedData NULL = new AbstractPersistedData() { @Override public boolean isNull() { return true; diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java index 1c3381ac714..4039bbeab97 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryReader.java @@ -3,6 +3,8 @@ package org.terasology.persistence.typeHandling.inMemory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.terasology.persistence.serializers.PersistedDataReader; import java.io.ByteArrayInputStream; @@ -12,13 +14,16 @@ import java.nio.ByteBuffer; public class InMemoryReader implements PersistedDataReader { + private static final Logger logger = LoggerFactory.getLogger(InMemoryReader.class); + @Override public AbstractPersistedData read(InputStream inputStream) throws IOException { ObjectInputStream ois = new ObjectInputStream(inputStream); try { return (AbstractPersistedData) ois.readObject(); } catch (ClassNotFoundException e) { - return null; // TODO + logger.error("Cannot read to inputStream"); + return InMemoryPersistedDataSerializer.NULL; } } diff --git a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java index 7bc6f15e1d6..2800be2f757 100644 --- a/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java +++ b/subsystems/TypeHandlerLibrary/src/main/java/org/terasology/persistence/typeHandling/inMemory/InMemoryWriter.java @@ -3,6 +3,8 @@ package org.terasology.persistence.typeHandling.inMemory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.terasology.persistence.serializers.PersistedDataWriter; import java.io.ByteArrayOutputStream; @@ -12,13 +14,15 @@ import java.nio.ByteBuffer; public class InMemoryWriter implements PersistedDataWriter { + private static final Logger logger = LoggerFactory.getLogger(InMemoryWriter.class); + @Override public byte[] writeBytes(AbstractPersistedData data) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - writeTo(data, baos); // TODO! + writeTo(data, baos); } catch (IOException e) { - e.printStackTrace(); + logger.error("Cannot writeBytes", e); } return baos.toByteArray(); } From 652076c8d53580ef24739e775d023eccca6a0fe7 Mon Sep 17 00:00:00 2001 From: darkweird Date: Mon, 29 Nov 2021 16:13:28 +0300 Subject: [PATCH 9/9] fix(ByteBuffer-Serializer): fix pmd's issues --- .../FullSerializationDeserializationTest.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java index b886d11e795..4a0181646a3 100644 --- a/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java +++ b/subsystems/TypeHandlerLibrary/src/test/java/org/terasology/persistence/typeHandling/FullSerializationDeserializationTest.java @@ -12,6 +12,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.terasology.persistence.serializers.Serializer; import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferDataReader; import org.terasology.persistence.typeHandling.bytebuffer.ByteBufferDataWriter; @@ -33,6 +35,8 @@ public class FullSerializationDeserializationTest { + private static final Logger logger = LoggerFactory.getLogger(FullSerializationDeserializationTest.class); + private static Reflections reflections; private static TypeHandlerLibrary typeHandlerLibrary; @@ -128,7 +132,7 @@ void test(Serializer serializer, TypeInfo type, T value) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertEquals(value, deserialized.get()); @@ -143,7 +147,7 @@ void testBoolArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -157,7 +161,7 @@ void testByteArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -171,7 +175,7 @@ void testIntArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -185,7 +189,7 @@ void testLongArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -199,7 +203,7 @@ void testFloatArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -213,7 +217,7 @@ void testDoubleArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -227,7 +231,7 @@ void testCharArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -241,7 +245,7 @@ void testObjectArray(Serializer serializer) { Optional serialized = serializer.serialize(value, type); Assertions.assertTrue(serialized.isPresent(), String.format("Serializer didn't serialize type %s", type)); byte[] bytes = serialized.get(); - System.out.println("Size in bytes: " + bytes.length); + logger.info("Size in bytes: {}", bytes.length); Optional deserialized = serializer.deserialize(type, bytes); Assertions.assertTrue(deserialized.isPresent(), String.format("Serializer didn't deserialize type %s", type)); Assertions.assertArrayEquals(value, deserialized.get()); @@ -275,7 +279,10 @@ public boolean equals(Object o) { return false; } SampleClass that = (SampleClass) o; - return value1 == that.value1 && Objects.equals(name, that.name) && Objects.equals(child, that.child) && Objects.equals(children, that.children); + return value1 == that.value1 + && Objects.equals(name, that.name) + && Objects.equals(child, that.child) + && Objects.equals(children, that.children); } @Override