From ead10cc155349b8fc13885dd12c7457c5aa1faea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 4 Oct 2020 02:03:31 +0200 Subject: [PATCH] Make NBTSerialization reuse existing arrays in deserialization. --- .../oc2/serialization/NBTSerialization.java | 100 +++++++++++++----- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/src/main/java/li/cil/oc2/serialization/NBTSerialization.java b/src/main/java/li/cil/oc2/serialization/NBTSerialization.java index 50d32a37..4e0ce803 100644 --- a/src/main/java/li/cil/oc2/serialization/NBTSerialization.java +++ b/src/main/java/li/cil/oc2/serialization/NBTSerialization.java @@ -14,23 +14,32 @@ import java.lang.reflect.Array; import java.util.UUID; public final class NBTSerialization { - private static final String IS_NULL_SUFFIX = ".is_null"; - - public static CompoundNBT serialize(final Object value) throws SerializationException { - final CompoundNBT nbt = new CompoundNBT(); - Ceres.getSerializer(value.getClass()).serialize(new Serializer(nbt), value); - return nbt; + public static void serialize(final CompoundNBT nbt, final T value) throws SerializationException { + @SuppressWarnings("unchecked") final Class type = (Class) value.getClass(); + Ceres.getSerializer(type).serialize(new Serializer(nbt), type, value); } - @SuppressWarnings("unchecked") - public static T deserialize(final CompoundNBT nbt, final T into) throws SerializationException { - return deserialize(nbt, (Class) into.getClass(), into); + public static CompoundNBT serialize(final T value) throws SerializationException { + final CompoundNBT nbt = new CompoundNBT(); + serialize(nbt, value); + return nbt; } public static T deserialize(final CompoundNBT nbt, final Class type, @Nullable final T into) throws SerializationException { return Ceres.getSerializer(type).deserialize(new Deserializer(nbt), type, into); } + public static T deserialize(final CompoundNBT nbt, final Class type) throws SerializationException { + return deserialize(nbt, type, null); + } + + public static T deserialize(final CompoundNBT nbt, final T into) throws SerializationException { + @SuppressWarnings("unchecked") final Class type = (Class) into.getClass(); + return deserialize(nbt, type, into); + } + + private static final String IS_NULL_KEY = ""; + private static final class Serializer implements SerializationVisitor { private final CompoundNBT nbt; @@ -134,7 +143,7 @@ public final class NBTSerialization { for (final Object datum : data) { final CompoundNBT itemNBT = new CompoundNBT(); if (datum == null) { - itemNBT.putBoolean(IS_NULL_SUFFIX, true); + itemNBT.putBoolean(IS_NULL_KEY, true); } else { if (datum.getClass() != componentType) { throw new SerializationException(String.format("Polymorphism detected in generic array [%s]. This is not supported.", name)); @@ -164,7 +173,11 @@ public final class NBTSerialization { @Contract(value = "_, null -> true") private boolean putIsNull(final String name, @Nullable final Object value) { final boolean isNull = value == null; - nbt.putBoolean(name + IS_NULL_SUFFIX, isNull); + if (isNull) { + final CompoundNBT nullNBT = new CompoundNBT(); + nullNBT.putBoolean(IS_NULL_KEY, true); + nbt.put(name, nullNBT); + } return isNull; } } @@ -231,41 +244,74 @@ public final class NBTSerialization { if (type == boolean[].class) { final byte[] convertedData = nbt.getByteArray(name); - final boolean[] data = new boolean[convertedData.length]; + boolean[] data = (boolean[]) into; + if (data == null || data.length != convertedData.length) { + data = new boolean[convertedData.length]; + } for (int i = 0; i < convertedData.length; i++) { data[i] = convertedData[i] != 0; } return data; } else if (type == byte[].class) { - return nbt.getByteArray(name); + final byte[] serializedData = nbt.getByteArray(name); + final byte[] data = (byte[]) into; + if (data == null || data.length != serializedData.length) { + return serializedData; + } + System.arraycopy(serializedData, 0, data, 0, serializedData.length); + return data; } else if (type == char[].class) { final int[] convertedData = nbt.getIntArray(name); - final char[] data = new char[convertedData.length]; + char[] data = (char[]) into; + if (data == null || data.length != convertedData.length) { + data = new char[convertedData.length]; + } for (int i = 0; i < convertedData.length; i++) { data[i] = (char) convertedData[i]; } return data; } else if (type == short[].class) { final int[] convertedData = nbt.getIntArray(name); - final short[] data = new short[convertedData.length]; + short[] data = (short[]) into; + if (data == null || data.length != convertedData.length) { + data = new short[convertedData.length]; + } for (int i = 0; i < convertedData.length; i++) { data[i] = (short) convertedData[i]; } return data; } else if (type == int[].class) { - return nbt.getIntArray(name); + final int[] serializedData = nbt.getIntArray(name); + final int[] data = (int[]) into; + if (data == null || data.length != serializedData.length) { + return serializedData; + } + System.arraycopy(serializedData, 0, data, 0, serializedData.length); + return data; } else if (type == long[].class) { - return nbt.getLongArray(name); + final long[] serializedData = nbt.getLongArray(name); + final long[] data = (long[]) into; + if (data == null || data.length != serializedData.length) { + return serializedData; + } + System.arraycopy(serializedData, 0, data, 0, serializedData.length); + return data; } else if (type == float[].class) { final int[] convertedData = nbt.getIntArray(name); - final float[] data = new float[convertedData.length]; + float[] data = (float[]) into; + if (data == null || data.length != convertedData.length) { + data = new float[convertedData.length]; + } for (int i = 0; i < convertedData.length; i++) { data[i] = Float.intBitsToFloat(convertedData[i]); } return data; } else if (type == double[].class) { final long[] convertedData = nbt.getLongArray(name); - final double[] data = new double[convertedData.length]; + double[] data = (double[]) into; + if (data == null || data.length != convertedData.length) { + data = new double[convertedData.length]; + } for (int i = 0; i < convertedData.length; i++) { data[i] = Double.longBitsToDouble(convertedData[i]); } @@ -274,14 +320,18 @@ public final class NBTSerialization { final Class componentType = type.getComponentType(); final li.cil.ceres.api.Serializer serializer = Ceres.getSerializer(componentType); final ListNBT listNBT = nbt.getList(name, Constants.NBT.TAG_COMPOUND); - final Object[] data = (Object[]) Array.newInstance(componentType, listNBT.size()); - for (int i = 0; i < listNBT.size(); i++) { + final int length = listNBT.size(); + Object[] data = (Object[]) into; + if (data == null || data.length != length) { + data = (Object[]) Array.newInstance(componentType, length); + } + for (int i = 0; i < length; i++) { final CompoundNBT itemNBT = listNBT.getCompound(i); - if (itemNBT.contains(IS_NULL_SUFFIX)) { + if (itemNBT.contains(IS_NULL_KEY)) { continue; } - data[i] = serializer.deserialize(new Deserializer(itemNBT), (Class) componentType, null); + data[i] = serializer.deserialize(new Deserializer(itemNBT), (Class) componentType, data[i]); } return data; } else if (type.isEnum()) { @@ -298,11 +348,11 @@ public final class NBTSerialization { @Override public boolean exists(final String name) { - return nbt.contains(name) || nbt.contains(name + IS_NULL_SUFFIX); + return nbt.contains(name); } private boolean isNull(final String name) { - return nbt.getBoolean(name + IS_NULL_SUFFIX); + return nbt.getCompound(name).getBoolean(IS_NULL_KEY); } } }