Add ObjectSerialization class for advanced object serialization and deserialization with options for compression, Base64 encoding, and digest verification; include unit tests for various serialization scenarios

This commit is contained in:
extio-de
2025-12-22 13:13:48 +01:00
parent d06d8c9f0f
commit a6f5a0f95c
3 changed files with 297 additions and 1 deletions

View File

@@ -0,0 +1,135 @@
package de.extio.game_engine.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.function.Consumer;
import com.github.luben.zstd.ZstdInputStream;
import com.github.luben.zstd.ZstdOutputStream;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.dataformat.yaml.YAMLMapper;
public final class ObjectSerialization {
private static final ObjectMapper OBJECT_MAPPER;
static {
OBJECT_MAPPER = YAMLMapper.builder()
.findAndAddModules()
.build();
}
public byte[] serialize(final Object o, final boolean compress, final boolean base64, final boolean digest, final byte[] digestSalt, final Consumer<byte[]> digestConsumer) {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1 << 16);
OutputStream base64OutputStream = null;
ZstdOutputStream zstdOutputStream = null;
DigestOutputStream digestOutputStream = null;
try {
OutputStream stream = byteArrayOutputStream;
if (base64) {
stream = base64OutputStream = Base64.getEncoder().wrap(stream);
}
if (compress) {
stream = zstdOutputStream = new ZstdOutputStream(stream);
}
if (digest) {
stream = digestOutputStream = new DigestOutputStream(stream, MessageDigest.getInstance("SHA-256"));
}
OBJECT_MAPPER.writeValue(stream, o);
}
catch (IOException | NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage(), e);
}
finally {
if (digestOutputStream != null) {
try {
digestOutputStream.close();
}
catch (final IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
digestConsumer.accept(digestOutputStream.getMessageDigest().digest(digestSalt));
}
if (zstdOutputStream != null) {
try {
zstdOutputStream.close();
}
catch (final IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
if (base64OutputStream != null) {
try {
base64OutputStream.close();
}
catch (final IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
return byteArrayOutputStream.toByteArray();
}
public <T extends Object> T deserialize(final Class<T> clazz, final byte[] data, final boolean deserializeObjRefs, final boolean decompress, final boolean base64, final byte[] checkDigest, final byte[] digestSalt, final Consumer<Boolean> digestCheckConsumer) {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
InputStream base64InputStream = null;
ZstdInputStream zstdInputStream = null;
DigestInputStream digestInputStream = null;
try {
InputStream stream = byteArrayInputStream;
if (base64) {
stream = base64InputStream = Base64.getDecoder().wrap(stream);
}
if (decompress) {
stream = zstdInputStream = new ZstdInputStream(stream);
}
if (checkDigest != null && checkDigest.length > 0) {
stream = digestInputStream = new DigestInputStream(stream, MessageDigest.getInstance("SHA-256"));
}
return OBJECT_MAPPER.readValue(stream, clazz);
}
catch (NoSuchAlgorithmException | IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
finally {
if (base64InputStream != null) {
try {
base64InputStream.close();
}
catch (final IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
if (zstdInputStream != null) {
try {
zstdInputStream.close();
}
catch (final IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
if (digestInputStream != null) {
try {
digestInputStream.close();
digestCheckConsumer.accept(Arrays.equals(checkDigest, digestInputStream.getMessageDigest().digest(digestSalt)));
}
catch (final IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
}