, Integer> messageTypeIds = new HashMap<>();
+ private final Map, RecordInfo>> messageRecordInfo = new HashMap<>();
+
+ public void registerType(int id, Class> type) {
+ if (!type.isRecord()) throw new IllegalArgumentException("Only records are permitted.");
+ this.messageTypes.put(id, type);
+ this.messageTypeIds.put(type, id);
+ this.messageRecordInfo.put(type, RecordInfo.forType(type));
+ }
+
+ public boolean isTypeSupported(Class> type) {
+ return messageTypeIds.containsKey(type);
+ }
+
+ @Override
+ public Object readMessage(InputStream in) throws IOException {
+ var dIn = new DataInputStream(in);
+ int id = dIn.readInt();
+ Class> msgType = messageTypes.get(id);
+ if (msgType == null) throw new UnknownMessageIdException(id);
+ return readRawObject(dIn, msgType);
+ }
+
+ private Object readRawObject(DataInputStream dIn, Class> type) throws IOException {
+ if (messageRecordInfo.containsKey(type)) {
+ RecordInfo> recordInfo = messageRecordInfo.get(type);
+ Object[] values = new Object[recordInfo.components().length];
+ for (int i = 0; i < recordInfo.components().length; i++) {
+ values[i] = readRawObject(dIn, recordInfo.components()[i].getType());
+ }
+ try {
+ return recordInfo.constructor().newInstance(values);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ if (type.isArray()) {
+ if (type.getComponentType().isPrimitive()) {
+ return IOUtil.readPrimitiveArray(type, dIn);
+ } else {
+ int length = dIn.readInt();
+ Object[] array = new Object[length];
+ for (int i = 0; i < length; i++) {
+ array[i] = readRawObject(dIn, type.getComponentType());
+ }
+ return array;
+ }
+ }
+ if (type.isEnum()) {
+ return IOUtil.readEnum(type, dIn);
+ }
+ if (type.equals(UUID.class)) {
+ return IOUtil.readUUID(dIn);
+ }
+ return IOUtil.readPrimitive(type, dIn);
+ }
+
+ @Override
+ public void writeMessage(Object msg, OutputStream out) throws IOException {
+ if (msg == null) throw new IllegalArgumentException("Cannot write a null message.");
+ if (!isTypeSupported(msg.getClass())) throw new UnsupportedMessageTypeException(msg.getClass());
+ var dOut = new DataOutputStream(out);
+ int id = messageTypeIds.get(msg.getClass());
+ dOut.writeInt(id);
+ writeRawObject(msg, dOut);
+ }
+
+ private void writeRawObject(Object obj, DataOutputStream dOut) throws IOException {
+ final Class> type = obj.getClass();
+ if (messageRecordInfo.containsKey(type)) {
+ RecordInfo> recordInfo = messageRecordInfo.get(type);
+ for (var component : recordInfo.components()) {
+ try {
+ writeRawObject(component.getAccessor().invoke(obj), dOut);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ } else if (type.isArray()) {
+ if (type.getComponentType().isPrimitive()) {
+ IOUtil.writePrimitiveArray(obj, dOut);
+ } else {
+ int length = Array.getLength(obj);
+ dOut.writeInt(length);
+ for (int i = 0; i < length; i++) {
+ writeRawObject(Array.get(obj, i), dOut);
+ }
+ }
+ } else if (type.isEnum()) {
+ IOUtil.writeEnum((Enum>) obj, dOut);
+ } else if (type.equals(UUID.class)) {
+ IOUtil.writeUUID((UUID) obj, dOut);
+ } else {
+ IOUtil.writePrimitive(obj, dOut);
+ }
+ }
+}
diff --git a/src/main/java/com/andrewlalis/record_net/RecordSerializer.java b/src/main/java/com/andrewlalis/record_net/RecordSerializer.java
new file mode 100644
index 0000000..f91b1fe
--- /dev/null
+++ b/src/main/java/com/andrewlalis/record_net/RecordSerializer.java
@@ -0,0 +1,10 @@
+package com.andrewlalis.record_net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public interface RecordSerializer {
+ Object readMessage(InputStream in) throws IOException;
+ void writeMessage(Object msg, OutputStream out) throws IOException;
+}
diff --git a/src/main/java/com/andrewlalis/record_net/UnknownMessageIdException.java b/src/main/java/com/andrewlalis/record_net/UnknownMessageIdException.java
new file mode 100644
index 0000000..d155d22
--- /dev/null
+++ b/src/main/java/com/andrewlalis/record_net/UnknownMessageIdException.java
@@ -0,0 +1,10 @@
+package com.andrewlalis.record_net;
+
+public class UnknownMessageIdException extends RuntimeException {
+ public final int messageId;
+
+ public UnknownMessageIdException(int messageId) {
+ super("Unknown record-net message id " + messageId);
+ this.messageId = messageId;
+ }
+}
diff --git a/src/main/java/com/andrewlalis/record_net/UnsupportedMessageTypeException.java b/src/main/java/com/andrewlalis/record_net/UnsupportedMessageTypeException.java
new file mode 100644
index 0000000..459c5d0
--- /dev/null
+++ b/src/main/java/com/andrewlalis/record_net/UnsupportedMessageTypeException.java
@@ -0,0 +1,16 @@
+package com.andrewlalis.record_net;
+
+public class UnsupportedMessageTypeException extends RuntimeException {
+ public Class> messageType;
+ public int messageId;
+
+ public UnsupportedMessageTypeException(Class> messageType) {
+ super("The message type " + messageType.getSimpleName() + " is not supported.");
+ this.messageType = messageType;
+ }
+
+ public UnsupportedMessageTypeException(int messageId) {
+ super("The message with id " + messageId + " is not supported.");
+ this.messageId = messageId;
+ }
+}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..3a4b7f8
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,3 @@
+module com.andrewlalis.record_net {
+ exports com.andrewlalis.record_net;
+}
\ No newline at end of file
diff --git a/src/main/java/nl/andrewl/record_net/Message.java b/src/main/java/nl/andrewl/record_net/Message.java
deleted file mode 100644
index 1c7fc7c..0000000
--- a/src/main/java/nl/andrewl/record_net/Message.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package nl.andrewl.record_net;
-
-/**
- * Represents any message which can be sent over the network.
- *
- * All messages consist of a single byte type identifier, followed by a
- * payload whose structure depends on the message.
- *
- */
-public interface Message {
- /**
- * Convenience method to get the serializer for this message's type, using
- * the static auto-generated set of serializers.
- * @param The message type.
- * @return The serializer to use to read and write messages of this type.
- */
- @SuppressWarnings("unchecked")
- default MessageTypeSerializer getTypeSerializer(Serializer serializer) {
- return (MessageTypeSerializer) serializer.getTypeSerializer(this.getClass());
- }
-}
diff --git a/src/main/java/nl/andrewl/record_net/MessageReader.java b/src/main/java/nl/andrewl/record_net/MessageReader.java
deleted file mode 100644
index a4130b8..0000000
--- a/src/main/java/nl/andrewl/record_net/MessageReader.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package nl.andrewl.record_net;
-
-import nl.andrewl.record_net.util.ExtendedDataInputStream;
-
-import java.io.IOException;
-
-@FunctionalInterface
-public interface MessageReader{
- /**
- * Reads all of this message's properties from the given input stream.
- *
- * The single byte type identifier has already been read.
- *
- * @param in The input stream to read from.
- * @return The message that was read.
- * @throws IOException If an error occurs while reading.
- */
- T read(ExtendedDataInputStream in) throws IOException;
-}
diff --git a/src/main/java/nl/andrewl/record_net/MessageTypeSerializer.java b/src/main/java/nl/andrewl/record_net/MessageTypeSerializer.java
deleted file mode 100644
index 16bb5a2..0000000
--- a/src/main/java/nl/andrewl/record_net/MessageTypeSerializer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package nl.andrewl.record_net;
-
-import java.util.function.Function;
-
-/**
- * A type serializer provides the basic components needed to read and write
- * instances of the given message type.
- * @param The message type.
- */
-public interface MessageTypeSerializer {
- /**
- * Gets the class of the message type that this serializer handles.
- * @return The message class.
- */
- Class messageClass();
-
- /**
- * Gets a function that computes the size, in bytes, of messages of this
- * serializer's type.
- * @return A byte size function.
- */
- Function byteSizeFunction();
-
- /**
- * Gets a component that can read messages from an input stream.
- * @return The message reader.
- */
- MessageReader reader();
-
- /**
- * Gets a component that can write messages to an output stream.
- * @return The message writer.
- */
- MessageWriter writer();
-}
diff --git a/src/main/java/nl/andrewl/record_net/MessageTypeSerializerImpl.java b/src/main/java/nl/andrewl/record_net/MessageTypeSerializerImpl.java
deleted file mode 100644
index 4d3a2a6..0000000
--- a/src/main/java/nl/andrewl/record_net/MessageTypeSerializerImpl.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package nl.andrewl.record_net;
-
-import java.util.function.Function;
-
-/**
- * Record containing the components needed to read and write a given message.
- * @param The type of message.
- * @param messageClass The class of the message.
- * @param byteSizeFunction A function that computes the byte size of the message.
- * @param reader A reader that can read messages from an input stream.
- * @param writer A writer that write messages from an input stream.
- */
-public record MessageTypeSerializerImpl(
- Class messageClass,
- Function byteSizeFunction,
- MessageReader reader,
- MessageWriter writer
-) implements MessageTypeSerializer {}
diff --git a/src/main/java/nl/andrewl/record_net/MessageUtils.java b/src/main/java/nl/andrewl/record_net/MessageUtils.java
deleted file mode 100644
index d3eb1e0..0000000
--- a/src/main/java/nl/andrewl/record_net/MessageUtils.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package nl.andrewl.record_net;
-
-import java.nio.charset.StandardCharsets;
-import java.util.UUID;
-
-/**
- * Utility class which provides methods for serializing and deserializing complex
- * data types.
- */
-public class MessageUtils {
- public static final int UUID_BYTES = 2 * Long.BYTES;
- public static final int ENUM_BYTES = Integer.BYTES;
-
- /**
- * Gets the number of bytes that the given string will occupy when it is
- * serialized.
- * @param s The string. This may be null.
- * @return The number of bytes used to serialize the string.
- */
- public static int getByteSize(String s) {
- return Integer.BYTES + (s == null ? 0 : s.getBytes(StandardCharsets.UTF_8).length);
- }
-
- /**
- * Gets the number of bytes that all the given strings will occupy when
- * serialized with a length-prefix encoding.
- * @param strings The set of strings.
- * @return The total byte size.
- */
- public static int getByteSize(String... strings) {
- int size = 0;
- for (var s : strings) {
- size += getByteSize(s);
- }
- return size;
- }
-
- @SuppressWarnings("unchecked")
- public static int getByteSize(Serializer serializer, T msg) {
- if (msg == null) {
- return 1;
- } else {
- MessageTypeSerializerImpl typeSerializer = (MessageTypeSerializerImpl) serializer.getTypeSerializer(msg.getClass());
- return 1 + typeSerializer.byteSizeFunction().apply(msg);
- }
- }
-
- public static int getByteSize(Serializer serializer, T[] items) {
- int count = Integer.BYTES;
- for (var item : items) {
- count += getByteSize(serializer, item);
- }
- return count;
- }
-
- public static int getByteSize(Serializer serializer, Object o) {
- if (o instanceof Integer) {
- return Integer.BYTES;
- } else if (o instanceof Float) {
- return Float.BYTES;
- } else if (o instanceof Byte) {
- return Byte.BYTES;
- } else if (o instanceof Double) {
- return Double.BYTES;
- } else if (o instanceof Short) {
- return Short.BYTES;
- } else if (o instanceof Long) {
- return Long.BYTES;
- } else if (o instanceof Boolean) {
- return 1;
- } else if (o instanceof String) {
- return getByteSize((String) o);
- } else if (o instanceof UUID) {
- return UUID_BYTES;
- } else if (o instanceof Enum>) {
- return ENUM_BYTES;
- } else if (o instanceof byte[]) {
- return Integer.BYTES + ((byte[]) o).length;
- } else if (o.getClass().isArray() && Message.class.isAssignableFrom(o.getClass().getComponentType())) {
- return getByteSize(serializer, (Message[]) o);
- } else if (o instanceof Message) {
- return getByteSize(serializer, (Message) o);
- } else {
- throw new IllegalArgumentException("Unsupported object type: " + o.getClass().getSimpleName());
- }
- }
-
- public static int getByteSize(Serializer serializer, Object... objects) {
- int size = 0;
- for (var o : objects) {
- size += getByteSize(serializer, o);
- }
- return size;
- }
-}
diff --git a/src/main/java/nl/andrewl/record_net/MessageWriter.java b/src/main/java/nl/andrewl/record_net/MessageWriter.java
deleted file mode 100644
index b80decf..0000000
--- a/src/main/java/nl/andrewl/record_net/MessageWriter.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package nl.andrewl.record_net;
-
-import nl.andrewl.record_net.util.ExtendedDataOutputStream;
-
-import java.io.IOException;
-
-@FunctionalInterface
-public interface MessageWriter {
- /**
- * Writes this message to the given output stream.
- * @param msg The message to write.
- * @param out The output stream to write to.
- * @throws IOException If an error occurs while writing.
- */
- void write(T msg, ExtendedDataOutputStream out) throws IOException;
-}
diff --git a/src/main/java/nl/andrewl/record_net/Serializer.java b/src/main/java/nl/andrewl/record_net/Serializer.java
deleted file mode 100644
index b3842f3..0000000
--- a/src/main/java/nl/andrewl/record_net/Serializer.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package nl.andrewl.record_net;
-
-import nl.andrewl.record_net.util.ExtendedDataInputStream;
-import nl.andrewl.record_net.util.ExtendedDataOutputStream;
-import nl.andrewl.record_net.util.RecordMessageTypeSerializer;
-
-import java.io.*;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * This class is responsible for reading and writing messages from streams. It
- * also defines the set of supported message types, and their associated byte
- * identifiers, via the {@link Serializer#registerType(int, Class)} method.
- */
-public class Serializer {
- /**
- * The mapping which defines each supported message type and the byte value
- * used to identify it when reading and writing messages.
- */
- private final Map> messageTypes = new HashMap<>();
-
- private final Map, MessageTypeSerializer>> messageTypeClasses = new HashMap<>();
-
- /**
- * An inverse of {@link Serializer#messageTypes} which is used to look up a
- * message's byte value when you know the class of the message.
- */
- private final Map, Byte> inverseMessageTypes = new HashMap<>();
-
- /**
- * Constructs a new serializer instance.
- */
- public Serializer() {}
-
- /**
- * Constructs a serializer using a predefined mapping of message types and
- * their ids.
- * @param messageTypes A map containing message types mapped to their ids.
- */
- public Serializer(Map> messageTypes) {
- messageTypes.forEach(this::registerType);
- }
-
- /**
- * Helper method for registering a message type serializer for a record
- * class, using {@link RecordMessageTypeSerializer#generateForRecord(Serializer, Class)}.
- * @param id The byte which will be used to identify messages of the given
- * class. The value should from 0 to 127.
- * @param messageClass The type of message associated with the given id.
- */
- public synchronized void registerType(int id, Class messageClass) {
- registerTypeSerializer(id, RecordMessageTypeSerializer.generateForRecord(this, messageClass));
- }
-
- /**
- * Registers the given type serializer with the given id.
- * @param id The id to use.
- * @param typeSerializer The type serializer that will be associated with
- * the given id.
- * @param The message type.
- */
- public synchronized void registerTypeSerializer(int id, MessageTypeSerializer typeSerializer) {
- if (id < 0 || id > 127) throw new IllegalArgumentException("Invalid id.");
- messageTypes.put((byte) id, typeSerializer);
- inverseMessageTypes.put(typeSerializer, (byte) id);
- messageTypeClasses.put(typeSerializer.messageClass(), typeSerializer);
- }
-
- /**
- * Gets the {@link MessageTypeSerializer} for the given message class.
- * @param messageType The class of message to get the serializer for.
- * @return The message type serializer.
- * @param The type of message.
- */
- @SuppressWarnings("unchecked")
- public MessageTypeSerializer getTypeSerializer(Class messageType) {
- return (MessageTypeSerializer) messageTypeClasses.get(messageType);
- }
-
- /**
- * Reads a message from the given input stream and returns it, or throws an
- * exception if an error occurred while reading from the stream.
- * @param i The input stream to read from.
- * @return The message which was read.
- * @throws IOException If an error occurs while reading, such as trying to
- * read an unsupported message type, or if a message object could not be
- * constructed for the incoming data.
- */
- public Message readMessage(InputStream i) throws IOException {
- return readMessage(new ExtendedDataInputStream(this, i));
- }
-
- public Message readMessage(ExtendedDataInputStream in) throws IOException {
- byte typeId = in.readByte();
- var type = messageTypes.get(typeId);
- if (type == null) {
- throw new IOException("Unsupported message type: " + typeId);
- }
- try {
- return type.reader().read(in);
- } catch (IOException e) {
- throw new IOException("Could not instantiate new message object of type " + type.getClass().getSimpleName(), e);
- }
- }
-
- /**
- * Reads a message from the given byte array and returns it, or throws an
- * exception if an error occurred while reading from the stream.
- * @param data The data to read from.
- * @return The message which was read.
- * @throws IOException If an error occurs while reading, such as trying to
- * read an unsupported message type, or if a message object could not be
- * constructed for the incoming data.
- */
- public Message readMessage(byte[] data) throws IOException {
- return readMessage(new ByteArrayInputStream(data));
- }
-
- /**
- * Writes a message to the given output stream.
- * @param msg The message to write.
- * @param o The output stream to write to.
- * @param The message type.
- * @throws IOException If an error occurs while writing, or if the message
- * to write is not supported by this serializer.
- */
- public void writeMessage(T msg, OutputStream o) throws IOException {
- writeMessage(msg, new ExtendedDataOutputStream(this, o));
- }
-
- public void writeMessage(T msg, ExtendedDataOutputStream out) throws IOException {
- Byte typeId = inverseMessageTypes.get(msg.getTypeSerializer(this));
- if (typeId == null) {
- throw new IOException("Unsupported message type: " + msg.getClass().getSimpleName());
- }
- out.writeByte(typeId);
- msg.getTypeSerializer(this).writer().write(msg, out);
- out.flush();
- }
-
- /**
- * Writes a message as a byte array.
- * @param msg The message to write.
- * @return The bytes that were written.
- * @param The message type.
- * @throws IOException If an error occurs while writing, or if the message
- * to write is not supported by this serializer.
- */
- public byte[] writeMessage(T msg) throws IOException {
- int bytes = msg.getTypeSerializer(this).byteSizeFunction().apply(msg);
- ByteArrayOutputStream out = new ByteArrayOutputStream(1 + bytes);
- writeMessage(msg, out);
- return out.toByteArray();
- }
-}
diff --git a/src/main/java/nl/andrewl/record_net/package-info.java b/src/main/java/nl/andrewl/record_net/package-info.java
deleted file mode 100644
index a31b99e..0000000
--- a/src/main/java/nl/andrewl/record_net/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * Main package containing notably the {@link nl.andrewl.record_net.Serializer}
- * and other components that may be of use for more fine-grained control.
- */
-package nl.andrewl.record_net;
\ No newline at end of file
diff --git a/src/main/java/nl/andrewl/record_net/util/ExtendedDataInputStream.java b/src/main/java/nl/andrewl/record_net/util/ExtendedDataInputStream.java
deleted file mode 100644
index 768b8e0..0000000
--- a/src/main/java/nl/andrewl/record_net/util/ExtendedDataInputStream.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package nl.andrewl.record_net.util;
-
-import nl.andrewl.record_net.Message;
-import nl.andrewl.record_net.MessageTypeSerializer;
-import nl.andrewl.record_net.Serializer;
-
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Array;
-import java.nio.charset.StandardCharsets;
-import java.util.UUID;
-
-/**
- * An extended output stream which contains additional methods for reading more
- * complex types that are used by the Concord system.
- */
-public class ExtendedDataInputStream extends DataInputStream {
- private final Serializer serializer;
-
- public ExtendedDataInputStream(Serializer serializer, InputStream in) {
- super(in);
- this.serializer = serializer;
- }
-
- public ExtendedDataInputStream(Serializer serializer, byte[] data) {
- this(serializer, new ByteArrayInputStream(data));
- }
-
- public String readString() throws IOException {
- int length = super.readInt();
- if (length == -1) return null;
- if (length == 0) return "";
- byte[] data = new byte[length];
- int read = super.read(data);
- if (read != length) throw new IOException("Not all bytes of a string of length " + length + " could be read.");
- return new String(data, StandardCharsets.UTF_8);
- }
-
- public > T readEnum(Class e) throws IOException {
- int ordinal = super.readInt();
- if (ordinal == -1) return null;
- return e.getEnumConstants()[ordinal];
- }
-
- public UUID readUUID() throws IOException {
- long a = super.readLong();
- long b = super.readLong();
- if (a == -1 && b == -1) {
- return null;
- }
- return new UUID(a, b);
- }
-
- public byte[] readByteArray() throws IOException {
- int length = readInt();
- if (length < 0) return null;
- byte[] array = new byte[length];
- int readCount = read(array, 0, length);
- while (readCount < length) {
- readCount += read(array, readCount, length - readCount);
- }
- return array;
- }
-
- public int[] readIntArray() throws IOException {
- int length = readInt();
- if (length < 0) return null;
- int[] array = new int[length];
- for (int i = 0; i < length; i++) {
- array[i] = readInt();
- }
- return array;
- }
-
- public float[] readFloatArray() throws IOException {
- int length = readInt();
- if (length < 0) return null;
- float[] array = new float[length];
- for (int i = 0; i < length; i++) {
- array[i] = readFloat();
- }
- return array;
- }
-
- @SuppressWarnings("unchecked")
- public T[] readArray(MessageTypeSerializer type) throws IOException {
- int length = super.readInt();
- T[] array = (T[]) Array.newInstance(type.messageClass(), length);
- for (int i = 0; i < length; i++) {
- array[i] = type.reader().read(this);
- }
- return array;
- }
-
- /**
- * Reads an object from the stream that is of a certain expected type.
- * @param type The type of object to read.
- * @return The object that was read.
- * @throws IOException If an error occurs while reading.
- */
- @SuppressWarnings("unchecked")
- public Object readObject(Class> type) throws IOException {
- if (type.equals(Integer.class) || type.equals(int.class)) {
- return readInt();
- } else if (type.equals(Short.class) || type.equals(short.class)) {
- return readShort();
- } else if (type.equals(Byte.class) || type.equals(byte.class)) {
- return (byte) read();
- } else if (type.equals(Long.class) || type.equals(long.class)) {
- return readLong();
- } else if (type.equals(Float.class) || type.equals(float.class)) {
- return readFloat();
- } else if (type.equals(Double.class) || type.equals(double.class)) {
- return readDouble();
- } else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
- return readBoolean();
- } else if (type.equals(String.class)) {
- return readString();
- } else if (type.equals(UUID.class)) {
- return readUUID();
- } else if (type.isEnum()) {
- return readEnum((Class extends Enum>>) type);
- } else if (type.isAssignableFrom(byte[].class)) {
- return readByteArray();
- } else if (type.isAssignableFrom(int[].class)) {
- return readIntArray();
- } else if (type.isAssignableFrom(float[].class)) {
- return readFloatArray();
- } else if (type.isArray() && Message.class.isAssignableFrom(type.getComponentType())) {
- var messageType = RecordMessageTypeSerializer.get(serializer, (Class extends Message>) type.getComponentType());
- return readArray(messageType);
- } else if (Message.class.isAssignableFrom(type)) {
- var messageType = RecordMessageTypeSerializer.get(serializer, (Class extends Message>) type);
- return messageType.reader().read(this);
- } else {
- throw new IOException("Unsupported object type: " + type.getSimpleName());
- }
- }
-}
diff --git a/src/main/java/nl/andrewl/record_net/util/ExtendedDataOutputStream.java b/src/main/java/nl/andrewl/record_net/util/ExtendedDataOutputStream.java
deleted file mode 100644
index acd3a00..0000000
--- a/src/main/java/nl/andrewl/record_net/util/ExtendedDataOutputStream.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package nl.andrewl.record_net.util;
-
-import nl.andrewl.record_net.Message;
-import nl.andrewl.record_net.Serializer;
-
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.UUID;
-
-/**
- * An extended version of {@link DataOutputStream} with some extra methods
- * that help us to write more data.
- */
-public class ExtendedDataOutputStream extends DataOutputStream {
- private final Serializer serializer;
-
- public ExtendedDataOutputStream(Serializer serializer, OutputStream out) {
- super(out);
- this.serializer = serializer;
- }
-
- /**
- * Writes a string in length-prefixed form, where the 4-byte length of the
- * string is written, followed by exactly that many bytes. If the string
- * is null, then a length of -1 is written, and no bytes following it.
- * @param s The string to write.
- * @throws IOException If an error occurs while writing.
- */
- public void writeString(String s) throws IOException {
- if (s == null) {
- writeInt(-1);
- } else {
- writeInt(s.length());
- write(s.getBytes(StandardCharsets.UTF_8));
- }
- }
-
- public void writeStrings(String... strings) throws IOException {
- for (var s : strings) {
- writeString(s);
- }
- }
-
- /**
- * Writes an enum value as a 4-byte integer using the enum's ordinal
- * position, or -1 if the given value is null.
- * @param value The value to write.
- * @throws IOException If an error occurs while writing.
- */
- public void writeEnum(Enum> value) throws IOException {
- if (value == null) {
- writeInt(-1);
- } else {
- writeInt(value.ordinal());
- }
- }
-
- /**
- * Writes a UUID as a 16-byte value. If the given UUID is null, then -1
- * is written twice as two long (8 byte) values.
- * @param uuid The value to write.
- * @throws IOException If an error occurs while writing.
- */
- public void writeUUID(UUID uuid) throws IOException {
- if (uuid == null) {
- writeLong(-1);
- writeLong(-1);
- } else {
- writeLong(uuid.getMostSignificantBits());
- writeLong(uuid.getLeastSignificantBits());
- }
- }
-
- /**
- * Writes an array of messages using length-prefixed form. That is, we
- * first write a 4-byte integer length that specifies how many items are in
- * the array, followed by writing each element of the array. If the array
- * is null, a length of -1 is written.
- * @param array The array to write.
- * @param The type of items in the array.
- * @throws IOException If an error occurs while writing.
- */
- public void writeArray(T[] array) throws IOException {
- if (array == null) {
- writeInt(-1);
- } else {
- writeInt(array.length);
- for (var item : array) writeMessage(item);
- }
- }
-
- public void writeArray(byte[] array) throws IOException {
- if (array == null) {
- writeInt(-1);
- } else {
- writeInt(array.length);
- write(array);
- }
- }
-
- public void writeArray(int[] array) throws IOException {
- if (array == null) {
- writeInt(-1);
- } else {
- writeInt(array.length);
- for (var item : array) writeInt(item);
- }
- }
-
- public void writeArray(float[] array) throws IOException {
- if (array == null) {
- writeInt(-1);
- } else {
- writeInt(array.length);
- for (var item : array) writeFloat(item);
- }
- }
-
- /**
- * Writes a message using null-prefixed form. That is, we first write a
- * boolean value which is false only if the message is null. Then, if the
- * message is not null, we write it to the stream.
- * @param msg The message to write.
- * @param The type of the message.
- * @throws IOException If an error occurs while writing.
- */
- public void writeMessage(Message msg) throws IOException {
- writeBoolean(msg != null);
- if (msg != null) {
- msg.getTypeSerializer(serializer).writer().write(msg, this);
- }
- }
-
- /**
- * Writes an object to the stream.
- * @param o The object to write.
- * @param type The object's type. This is needed in case the object itself
- * is null, which may be the case for some strings or ids.
- * @throws IOException If an error occurs while writing, or if an
- * unsupported object is supplied.
- */
- public void writeObject(Object o, Class> type) throws IOException {
- if (type.equals(Integer.class) || type.equals(int.class)) {
- writeInt((int) o);
- } else if (type.equals(Short.class) || type.equals(short.class)) {
- writeShort((short) o);
- } else if (type.equals(Byte.class) || type.equals(byte.class)) {
- writeByte((byte) o);
- } else if (type.equals(Long.class) || type.equals(long.class)) {
- writeLong((long) o);
- } else if (type.equals(Float.class) || type.equals(float.class)) {
- writeFloat((float) o);
- } else if (type.equals(Double.class) || type.equals(double.class)) {
- writeDouble((double) o);
- } else if (type.equals(Boolean.class) || type.equals(boolean.class)) {
- writeBoolean((boolean) o);
- } else if (type.equals(String.class)) {
- writeString((String) o);
- } else if (type.equals(UUID.class)) {
- writeUUID((UUID) o);
- } else if (type.isEnum()) {
- writeEnum((Enum>) o);
- } else if (type.equals(byte[].class)) {
- writeArray((byte[]) o);
- } else if (type.equals(int[].class)) {
- writeArray((int[]) o);
- } else if (type.equals(float[].class)) {
- writeArray((float[]) o);
- } else if (type.isArray() && Message.class.isAssignableFrom(type.getComponentType())) {
- writeArray((Message[]) o);
- } else if (Message.class.isAssignableFrom(type)) {
- writeMessage((Message) o);
- } else {
- throw new IOException("Unsupported object type: " + o.getClass().getSimpleName());
- }
- }
-}
diff --git a/src/main/java/nl/andrewl/record_net/util/Pair.java b/src/main/java/nl/andrewl/record_net/util/Pair.java
deleted file mode 100644
index edc2206..0000000
--- a/src/main/java/nl/andrewl/record_net/util/Pair.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package nl.andrewl.record_net.util;
-
-/**
- * Simple generic pair of two objects.
- * @param The first object.
- * @param The second object.
- */
-public record Pair(A first, B second) {}
diff --git a/src/main/java/nl/andrewl/record_net/util/RecordMessageTypeSerializer.java b/src/main/java/nl/andrewl/record_net/util/RecordMessageTypeSerializer.java
deleted file mode 100644
index 45a5e16..0000000
--- a/src/main/java/nl/andrewl/record_net/util/RecordMessageTypeSerializer.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package nl.andrewl.record_net.util;
-
-import nl.andrewl.record_net.*;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.RecordComponent;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
-
-/**
- * Helper class that contains logic for generating {@link MessageTypeSerializerImpl}
- * implementations for record classes.
- */
-public class RecordMessageTypeSerializer {
- /**
- * An internal cache for storing generated type serializers.
- */
- private static final Map, Serializer>, MessageTypeSerializer>> generatedMessageTypes = new HashMap<>();
-
- /**
- * Gets the {@link MessageTypeSerializer} instance for a given message class, and
- * generates a new implementation if none exists yet.
- * @param serializer The serializer context to get a type serializer for.
- * @param messageClass The class of the message to get a type for.
- * @param The type of the message.
- * @return The message type.
- */
- @SuppressWarnings("unchecked")
- public static MessageTypeSerializer get(Serializer serializer, Class messageClass) {
- return (MessageTypeSerializer) generatedMessageTypes.computeIfAbsent(
- new Pair<>(messageClass, serializer),
- p -> generateForRecord(serializer, (Class) p.first())
- );
- }
-
- /**
- * Generates a message type instance for a given class, using reflection to
- * introspect the fields of the message.
- *
- * Note that this only works for record-based messages.
- *
- * @param serializer The serializer context to get a type serializer for.
- * @param messageTypeClass The class of the message type.
- * @param The type of the message.
- * @return A message type instance.
- */
- public static MessageTypeSerializerImpl generateForRecord(Serializer serializer, Class messageTypeClass) {
- RecordComponent[] components = messageTypeClass.getRecordComponents();
- if (components == null) throw new IllegalArgumentException("Cannot generate a MessageTypeSerializer for non-record class " + messageTypeClass.getSimpleName());
- Constructor constructor;
- try {
- constructor = messageTypeClass.getDeclaredConstructor(Arrays.stream(components)
- .map(RecordComponent::getType).toArray(Class>[]::new));
- } catch (NoSuchMethodException e) {
- throw new IllegalArgumentException(e);
- }
- return new MessageTypeSerializerImpl<>(
- messageTypeClass,
- generateByteSizeFunction(serializer, components),
- generateReader(constructor),
- generateWriter(components)
- );
- }
-
- /**
- * Generates a function implementation that counts the byte size of a
- * message based on the message's record component types.
- * @param serializer The serializer context to generate a function for.
- * @param components The list of components that make up the message.
- * @param The message type.
- * @return A function that computes the byte size of a message of the given
- * type.
- */
- private static Function generateByteSizeFunction(Serializer serializer, RecordComponent[] components) {
- return msg -> {
- int size = 0;
- for (var component : components) {
- try {
- size += MessageUtils.getByteSize(serializer, component.getAccessor().invoke(msg));
- } catch (ReflectiveOperationException e) {
- throw new IllegalStateException(e);
- }
- }
- return size;
- };
- }
-
- /**
- * Generates a message reader for the given message constructor method. It
- * will try to read objects from the input stream according to the
- * parameters of the canonical constructor of a message record.
- * @param constructor The canonical constructor of the message record.
- * @param The message type.
- * @return A message reader for the given type.
- */
- private static MessageReader generateReader(Constructor constructor) {
- return in -> {
- Object[] values = new Object[constructor.getParameterCount()];
- for (int i = 0; i < values.length; i++) {
- values[i] = in.readObject(constructor.getParameterTypes()[i]);
- }
- try {
- return constructor.newInstance(values);
- } catch (ReflectiveOperationException e) {
- throw new IllegalStateException(e);
- }
- };
- }
-
- /**
- * Generates a message writer for the given message record components.
- * @param components The record components to write.
- * @param The type of message.
- * @return The message writer for the given type.
- */
- private static MessageWriter generateWriter(RecordComponent[] components) {
- return (msg, out) -> {
- for (var component: components) {
- try {
- out.writeObject(component.getAccessor().invoke(msg), component.getType());
- } catch (ReflectiveOperationException e) {
- throw new IllegalStateException(e);
- }
- }
- };
- }
-}
diff --git a/src/main/java/nl/andrewl/record_net/util/Triple.java b/src/main/java/nl/andrewl/record_net/util/Triple.java
deleted file mode 100644
index ea8dc63..0000000
--- a/src/main/java/nl/andrewl/record_net/util/Triple.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package nl.andrewl.record_net.util;
-
-/**
- * Simple generic triple of objects.
- * @param The first object.
- * @param The second object.
- * @param The third object.
- */
-public record Triple (A first, B second, C third) {}
diff --git a/src/main/java/nl/andrewl/record_net/util/package-info.java b/src/main/java/nl/andrewl/record_net/util/package-info.java
deleted file mode 100644
index 2d9bf2f..0000000
--- a/src/main/java/nl/andrewl/record_net/util/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains some useful one-off utility classes.
- */
-package nl.andrewl.record_net.util;
\ No newline at end of file
diff --git a/src/test/java/com/andrewlalis/record_net/IOUtilTest.java b/src/test/java/com/andrewlalis/record_net/IOUtilTest.java
new file mode 100644
index 0000000..8748a3c
--- /dev/null
+++ b/src/test/java/com/andrewlalis/record_net/IOUtilTest.java
@@ -0,0 +1,39 @@
+package com.andrewlalis.record_net;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class IOUtilTest {
+ @Test
+ public void testReadPrimitive() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dOut = new DataOutputStream(baos);
+ dOut.writeInt(42);
+ dOut.writeShort(25565);
+ dOut.writeByte(35);
+ dOut.writeChar(234);
+ dOut.writeLong(234843209243L);
+ dOut.writeFloat(3.14f);
+ dOut.writeDouble(2.17);
+ dOut.writeBoolean(true);
+ dOut.writeBoolean(false);
+ byte[] data = baos.toByteArray();
+
+ DataInputStream dIn = new DataInputStream(new ByteArrayInputStream(data));
+ assertEquals(42, IOUtil.readPrimitive(Integer.class, dIn));
+ assertEquals((short) 25565, IOUtil.readPrimitive(Short.class, dIn));
+ assertEquals((byte) 35, IOUtil.readPrimitive(Byte.class, dIn));
+ assertEquals((char) 234, IOUtil.readPrimitive(Character.class, dIn));
+ assertEquals(234843209243L, IOUtil.readPrimitive(Long.class, dIn));
+ assertEquals(3.14f, IOUtil.readPrimitive(Float.class, dIn));
+ assertEquals(2.17, IOUtil.readPrimitive(Double.class, dIn));
+ assertEquals(true, IOUtil.readPrimitive(Boolean.class, dIn));
+ assertEquals(false, IOUtil.readPrimitive(Boolean.class, dIn));
+ }
+}
diff --git a/src/test/java/com/andrewlalis/record_net/RecordMappedSerializerTest.java b/src/test/java/com/andrewlalis/record_net/RecordMappedSerializerTest.java
new file mode 100644
index 0000000..5cdcacf
--- /dev/null
+++ b/src/test/java/com/andrewlalis/record_net/RecordMappedSerializerTest.java
@@ -0,0 +1,62 @@
+package com.andrewlalis.record_net;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class RecordMappedSerializerTest {
+ @Test
+ public void testRegisterType() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ new RecordMappedSerializer().registerType(1, String.class);
+ });
+ assertDoesNotThrow(() -> {
+ record TmpRecord (int a) {}
+ new RecordMappedSerializer().registerType(1, TmpRecord.class);
+ });
+ RecordMappedSerializer serializer = new RecordMappedSerializer();
+ record TmpRecord1 (int a) {}
+ assertFalse(serializer.isTypeSupported(TmpRecord1.class));
+ serializer.registerType(1, TmpRecord1.class);
+ assertTrue(serializer.isTypeSupported(TmpRecord1.class));
+ }
+
+ @Test
+ public void testBasicReadAndWrite() throws Exception {
+ record TmpRecordA (int a, float b, byte[] c) {}
+ RecordMappedSerializer serializer = new RecordMappedSerializer();
+ serializer.registerType(1, TmpRecordA.class);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ TmpRecordA testObj = new TmpRecordA(54, 234.543f, new byte[]{1, 2, 3});
+ serializer.writeMessage(testObj, baos);
+ byte[] data = baos.toByteArray();
+
+ Object obj = serializer.readMessage(new ByteArrayInputStream(data));
+ assertInstanceOf(TmpRecordA.class, obj);
+ TmpRecordA objA = (TmpRecordA) obj;
+ assertEquals(testObj.a, objA.a);
+ assertEquals(testObj.b, objA.b);
+ assertArrayEquals(testObj.c, objA.c);
+ }
+
+ @Test
+ public void testNestedRecords() throws Exception {
+ record RecordA (int a, int b, float x) {}
+ record RecordB (RecordA a, boolean flag) {}
+ RecordMappedSerializer serializer = new RecordMappedSerializer();
+ serializer.registerType(1, RecordA.class);
+ serializer.registerType(2, RecordB.class);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ RecordB testObj = new RecordB(new RecordA(1, 2, 3.5f), false);
+ serializer.writeMessage(testObj, baos);
+ byte[] data = baos.toByteArray();
+
+ Object obj = serializer.readMessage(new ByteArrayInputStream(data));
+ assertInstanceOf(RecordB.class, obj);
+ RecordB b = (RecordB) obj;
+ assertEquals(testObj, b);
+ }
+}
diff --git a/src/test/java/nl/andrewl/record_net/MessageUtilsTest.java b/src/test/java/nl/andrewl/record_net/MessageUtilsTest.java
deleted file mode 100644
index e155d9a..0000000
--- a/src/test/java/nl/andrewl/record_net/MessageUtilsTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package nl.andrewl.record_net;
-
-import nl.andrewl.record_net.msg.ChatMessage;
-import org.junit.jupiter.api.Test;
-
-import java.nio.file.StandardCopyOption;
-import java.util.UUID;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class MessageUtilsTest {
- @Test
- public void testGetByteSize() {
- assertEquals(4, MessageUtils.getByteSize((String) null));
- assertEquals(5, MessageUtils.getByteSize("a"));
- assertEquals(16, MessageUtils.getByteSize("Hello world!"));
- assertEquals(8, MessageUtils.getByteSize("", ""));
- assertEquals(10, MessageUtils.getByteSize("a", "b"));
- Message msg = new ChatMessage("andrew", 123, "Hello world!");
- int expectedMsgSize = 1 + 4 + 6 + 8 + 4 + 12;
- Serializer serializer = new Serializer();
- serializer.registerType(1, ChatMessage.class);
- assertEquals(1, MessageUtils.getByteSize(serializer, (Message) null));
- assertEquals(expectedMsgSize, MessageUtils.getByteSize(serializer, msg));
- assertEquals(4 * expectedMsgSize, MessageUtils.getByteSize(serializer, msg, msg, msg, msg));
- assertEquals(16, MessageUtils.getByteSize(serializer, UUID.randomUUID()));
- assertEquals(4, MessageUtils.getByteSize(serializer, StandardCopyOption.ATOMIC_MOVE));
- }
-}
diff --git a/src/test/java/nl/andrewl/record_net/RecordMessageTypeSerializerTest.java b/src/test/java/nl/andrewl/record_net/RecordMessageTypeSerializerTest.java
deleted file mode 100644
index b24e6e3..0000000
--- a/src/test/java/nl/andrewl/record_net/RecordMessageTypeSerializerTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package nl.andrewl.record_net;
-
-import nl.andrewl.record_net.msg.ChatMessage;
-import nl.andrewl.record_net.util.ExtendedDataInputStream;
-import nl.andrewl.record_net.util.ExtendedDataOutputStream;
-import nl.andrewl.record_net.util.RecordMessageTypeSerializer;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class RecordMessageTypeSerializerTest {
- @Test
- public void testGenerateForRecord() throws IOException {
- Serializer serializer = new Serializer();
- var s1 = RecordMessageTypeSerializer.get(serializer, ChatMessage.class);
- ChatMessage msg = new ChatMessage("andrew", 123, "Hello world!");
- int expectedByteSize = 4 + msg.username().length() + 8 + 4 + msg.message().length();
- assertEquals(expectedByteSize, s1.byteSizeFunction().apply(msg));
-
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- ExtendedDataOutputStream eOut = new ExtendedDataOutputStream(serializer, bOut);
- s1.writer().write(msg, eOut);
- byte[] data = bOut.toByteArray();
- assertEquals(expectedByteSize, data.length);
- ChatMessage readMsg = s1.reader().read(new ExtendedDataInputStream(serializer, data));
- assertEquals(msg, readMsg);
-
- // Only record classes can be generated.
- class NonRecordMessage implements Message {}
- assertThrows(IllegalArgumentException.class, () -> RecordMessageTypeSerializer.get(serializer, NonRecordMessage.class));
- }
-}
diff --git a/src/test/java/nl/andrewl/record_net/SerializerTest.java b/src/test/java/nl/andrewl/record_net/SerializerTest.java
deleted file mode 100644
index d6fcc16..0000000
--- a/src/test/java/nl/andrewl/record_net/SerializerTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package nl.andrewl.record_net;
-
-import nl.andrewl.record_net.msg.ChatMessage;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class SerializerTest {
- @Test
- public void testReadAndWriteMessage() throws IOException {
- Serializer s = new Serializer();
- s.registerType(1, ChatMessage.class);
-
- ChatMessage msg = new ChatMessage("andrew", 123, "Hello world!");
-
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- s.writeMessage(msg, bOut);
- byte[] data = bOut.toByteArray();
- assertEquals(MessageUtils.getByteSize(s, msg), data.length);
- assertEquals(data[0], 1);
-
- ChatMessage readMsg = (ChatMessage) s.readMessage(new ByteArrayInputStream(data));
- assertEquals(msg, readMsg);
- }
-}
diff --git a/src/test/java/nl/andrewl/record_net/msg/ChatMessage.java b/src/test/java/nl/andrewl/record_net/msg/ChatMessage.java
deleted file mode 100644
index 00f4123..0000000
--- a/src/test/java/nl/andrewl/record_net/msg/ChatMessage.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package nl.andrewl.record_net.msg;
-
-import nl.andrewl.record_net.Message;
-
-public record ChatMessage(
- String username,
- long timestamp,
- String message
-) implements Message {}
diff --git a/src/test/java/nl/andrewl/record_net/util/ExtendedDataOutputStreamTest.java b/src/test/java/nl/andrewl/record_net/util/ExtendedDataOutputStreamTest.java
deleted file mode 100644
index fbb9968..0000000
--- a/src/test/java/nl/andrewl/record_net/util/ExtendedDataOutputStreamTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package nl.andrewl.record_net.util;
-
-import nl.andrewl.record_net.Serializer;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class ExtendedDataOutputStreamTest {
-
- @Test
- public void testWriteString() throws IOException {
- Serializer serializer = new Serializer();
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- ExtendedDataOutputStream eOut = new ExtendedDataOutputStream(serializer, bOut);
- eOut.writeString("Hello world!");
- byte[] data = bOut.toByteArray();
- assertEquals(4 + "Hello world!".length(), data.length);
- DataInputStream dIn = new DataInputStream(new ByteArrayInputStream(data));
- assertEquals(12, dIn.readInt());
- String s = new String(dIn.readNBytes(12));
- assertEquals("Hello world!", s);
-
- bOut.reset();
- eOut.writeString(null);
- data = bOut.toByteArray();
- assertEquals(4, data.length);
- dIn = new DataInputStream(new ByteArrayInputStream(data));
- assertEquals(-1, dIn.readInt());
- }
-}