From 62d5509768f1a5101754a933ffebc89a00d3bd2b Mon Sep 17 00:00:00 2001 From: Steven Smith Date: Sat, 22 Aug 2015 21:43:32 -0700 Subject: [PATCH] Add additional array I/O methods, add ByteBuffer-backed NetInput and NetOutput implementations. --- .../org/spacehq/packetlib/io/NetInput.java | 87 ++++++ .../org/spacehq/packetlib/io/NetOutput.java | 51 ++++ .../io/buffer/ByteBufferNetInput.java | 261 ++++++++++++++++++ .../io/buffer/ByteBufferNetOutput.java | 153 ++++++++++ .../packetlib/io/stream/StreamNetInput.java | 99 +++++++ .../packetlib/io/stream/StreamNetOutput.java | 36 +++ .../packetlib/tcp/io/ByteBufNetInput.java | 111 ++++++++ .../packetlib/tcp/io/ByteBufNetOutput.java | 36 +++ 8 files changed, 834 insertions(+) create mode 100644 src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetInput.java create mode 100644 src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetOutput.java diff --git a/src/main/java/org/spacehq/packetlib/io/NetInput.java b/src/main/java/org/spacehq/packetlib/io/NetInput.java index 1e792a93..320b21ac 100644 --- a/src/main/java/org/spacehq/packetlib/io/NetInput.java +++ b/src/main/java/org/spacehq/packetlib/io/NetInput.java @@ -132,6 +132,93 @@ public interface NetInput { */ public int readBytes(byte b[], int offset, int length) throws IOException; + /** + * Reads the next short array. + * + * @param length The length of the short array. + * @return The next short array. + * @throws java.io.IOException If an I/O error occurs. + */ + public short[] readShorts(int length) throws IOException; + + /** + * Reads as much data as possible into the given short array. + * + * @param s Short array to read to. + * @return The amount of shorts read, or -1 if no shorts could be read. + * @throws java.io.IOException If an I/O error occurs. + */ + public int readShorts(short s[]) throws IOException; + + /** + * Reads the given amount of shorts into the given array at the given offset. + * + * @param s Short array to read to. + * @param offset Offset of the array to read to. + * @param length Length of bytes to read. + * @return The amount of shorts read, or -1 if no shorts could be read. + * @throws java.io.IOException If an I/O error occurs. + */ + public int readShorts(short s[], int offset, int length) throws IOException; + + /** + * Reads the next int array. + * + * @param length The length of the int array. + * @return The next int array. + * @throws java.io.IOException If an I/O error occurs. + */ + public int[] readInts(int length) throws IOException; + + /** + * Reads as much data as possible into the given int array. + * + * @param i Int array to read to. + * @return The amount of ints read, or -1 if no ints could be read. + * @throws java.io.IOException If an I/O error occurs. + */ + public int readInts(int i[]) throws IOException; + + /** + * Reads the given amount of ints into the given array at the given offset. + * + * @param i Int array to read to. + * @param offset Offset of the array to read to. + * @param length Length of bytes to read. + * @return The amount of ints read, or -1 if no ints could be read. + * @throws java.io.IOException If an I/O error occurs. + */ + public int readInts(int i[], int offset, int length) throws IOException; + + /** + * Reads the next long array. + * + * @param length The length of the long array. + * @return The next long array. + * @throws java.io.IOException If an I/O error occurs. + */ + public long[] readLongs(int length) throws IOException; + + /** + * Reads as much data as possible into the given long array. + * + * @param l Long array to read to. + * @return The amount of longs read, or -1 if no longs could be read. + * @throws java.io.IOException If an I/O error occurs. + */ + public int readLongs(long l[]) throws IOException; + + /** + * Reads the given amount of longs into the given array at the given offset. + * + * @param l Long array to read to. + * @param offset Offset of the array to read to. + * @param length Length of bytes to read. + * @return The amount of longs read, or -1 if no longs could be read. + * @throws java.io.IOException If an I/O error occurs. + */ + public int readLongs(long l[], int offset, int length) throws IOException; + /** * Reads the next string. * diff --git a/src/main/java/org/spacehq/packetlib/io/NetOutput.java b/src/main/java/org/spacehq/packetlib/io/NetOutput.java index 95323a8c..f30a60d0 100644 --- a/src/main/java/org/spacehq/packetlib/io/NetOutput.java +++ b/src/main/java/org/spacehq/packetlib/io/NetOutput.java @@ -104,6 +104,57 @@ public interface NetOutput { */ public void writeBytes(byte b[], int length) throws IOException; + /** + * Writes a short array. + * + * @param s Short array to write. + * @throws java.io.IOException If an I/O error occurs. + */ + public void writeShorts(short s[]) throws IOException; + + /** + * Writes a short array, using the given amount of bytes. + * + * @param s Short array to write. + * @param length Shorts to write. + * @throws java.io.IOException If an I/O error occurs. + */ + public void writeShorts(short s[], int length) throws IOException; + + /** + * Writes an int array. + * + * @param i Int array to write. + * @throws java.io.IOException If an I/O error occurs. + */ + public void writeInts(int i[]) throws IOException; + + /** + * Writes an int array, using the given amount of bytes. + * + * @param i Int array to write. + * @param length Ints to write. + * @throws java.io.IOException If an I/O error occurs. + */ + public void writeInts(int i[], int length) throws IOException; + + /** + * Writes a long array. + * + * @param l Long array to write. + * @throws java.io.IOException If an I/O error occurs. + */ + public void writeLongs(long l[]) throws IOException; + + /** + * Writes a long array, using the given amount of bytes. + * + * @param l Long array to write. + * @param length Longs to write. + * @throws java.io.IOException If an I/O error occurs. + */ + public void writeLongs(long l[], int length) throws IOException; + /** * Writes a string. * diff --git a/src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetInput.java b/src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetInput.java new file mode 100644 index 00000000..af2e051b --- /dev/null +++ b/src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetInput.java @@ -0,0 +1,261 @@ +package org.spacehq.packetlib.io.buffer; + +import org.spacehq.packetlib.io.NetInput; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * A NetInput implementation using a ByteBuffer as a backend. + */ +public class ByteBufferNetInput implements NetInput { + private ByteBuffer buffer; + + public ByteBufferNetInput(ByteBuffer buffer) { + this.buffer = buffer; + } + + public ByteBuffer getByteBuffer() { + return this.buffer; + } + + @Override + public boolean readBoolean() throws IOException { + return this.buffer.get() == 1; + } + + @Override + public byte readByte() throws IOException { + return this.buffer.get(); + } + + @Override + public int readUnsignedByte() throws IOException { + return this.buffer.get() & 0xFF; + } + + @Override + public short readShort() throws IOException { + return this.buffer.getShort(); + } + + @Override + public int readUnsignedShort() throws IOException { + return this.buffer.getShort() & 0xFFFF; + } + + @Override + public char readChar() throws IOException { + return this.buffer.getChar(); + } + + @Override + public int readInt() throws IOException { + return this.buffer.getInt(); + } + + @Override + public int readVarInt() throws IOException { + int value = 0; + int size = 0; + int b; + while(((b = this.readByte()) & 0x80) == 0x80) { + value |= (b & 0x7F) << (size++ * 7); + if(size > 5) { + throw new IOException("VarInt too long (length must be <= 5)"); + } + } + + return value | ((b & 0x7F) << (size * 7)); + } + + @Override + public long readLong() throws IOException { + return this.buffer.getLong(); + } + + @Override + public long readVarLong() throws IOException { + int value = 0; + int size = 0; + int b; + while(((b = this.readByte()) & 0x80) == 0x80) { + value |= (b & 0x7F) << (size++ * 7); + if(size > 10) { + throw new IOException("VarLong too long (length must be <= 10)"); + } + } + + return value | ((b & 0x7F) << (size * 7)); + } + + @Override + public float readFloat() throws IOException { + return this.buffer.getFloat(); + } + + @Override + public double readDouble() throws IOException { + return this.buffer.getDouble(); + } + + @Override + public byte[] readBytes(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + byte b[] = new byte[length]; + this.buffer.get(b); + return b; + } + + @Override + public int readBytes(byte[] b) throws IOException { + return this.readBytes(b, 0, b.length); + } + + @Override + public int readBytes(byte[] b, int offset, int length) throws IOException { + int readable = this.buffer.remaining(); + if(readable <= 0) { + return -1; + } + + if(readable < length) { + length = readable; + } + + this.buffer.get(b, offset, length); + return length; + } + + @Override + public short[] readShorts(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + short s[] = new short[length]; + for(int index = 0; index < length; index++) { + s[index] = this.readShort(); + } + + return s; + } + + @Override + public int readShorts(short[] s) throws IOException { + return this.readShorts(s, 0, s.length); + } + + @Override + public int readShorts(short[] s, int offset, int length) throws IOException { + int readable = this.buffer.remaining(); + if(readable <= 0) { + return -1; + } + + if(readable < length * 2) { + length = readable / 2; + } + + for(int index = offset; index < offset + length; index++) { + s[index] = this.readShort(); + } + + return length; + } + + @Override + public int[] readInts(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + int i[] = new int[length]; + for(int index = 0; index < length; index++) { + i[index] = this.readInt(); + } + + return i; + } + + @Override + public int readInts(int[] i) throws IOException { + return this.readInts(i, 0, i.length); + } + + @Override + public int readInts(int[] i, int offset, int length) throws IOException { + int readable = this.buffer.remaining(); + if(readable <= 0) { + return -1; + } + + if(readable < length * 4) { + length = readable / 4; + } + + for(int index = offset; index < offset + length; index++) { + i[index] = this.readInt(); + } + + return length; + } + + @Override + public long[] readLongs(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + long l[] = new long[length]; + for(int index = 0; index < length; index++) { + l[index] = this.readLong(); + } + + return l; + } + + @Override + public int readLongs(long[] l) throws IOException { + return this.readLongs(l, 0, l.length); + } + + @Override + public int readLongs(long[] l, int offset, int length) throws IOException { + int readable = this.buffer.remaining(); + if(readable <= 0) { + return -1; + } + + if(readable < length * 2) { + length = readable / 2; + } + + for(int index = offset; index < offset + length; index++) { + l[index] = this.readLong(); + } + + return length; + } + + @Override + public String readString() throws IOException { + int length = this.readVarInt(); + byte bytes[] = this.readBytes(length); + return new String(bytes, "UTF-8"); + } + + @Override + public UUID readUUID() throws IOException { + return new UUID(this.readLong(), this.readLong()); + } + + @Override + public int available() throws IOException { + return this.buffer.remaining(); + } +} diff --git a/src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetOutput.java b/src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetOutput.java new file mode 100644 index 00000000..be46f7f2 --- /dev/null +++ b/src/main/java/org/spacehq/packetlib/io/buffer/ByteBufferNetOutput.java @@ -0,0 +1,153 @@ +package org.spacehq.packetlib.io.buffer; + +import org.spacehq.packetlib.io.NetOutput; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * A NetOutput implementation using a ByteBuf as a backend. + */ +public class ByteBufferNetOutput implements NetOutput { + private ByteBuffer buffer; + + public ByteBufferNetOutput(ByteBuffer buffer) { + this.buffer = buffer; + } + + public ByteBuffer getByteBuffer() { + return this.buffer; + } + + @Override + public void writeBoolean(boolean b) throws IOException { + this.buffer.put(b ? (byte) 1 : 0); + } + + @Override + public void writeByte(int b) throws IOException { + this.buffer.put((byte) b); + } + + @Override + public void writeShort(int s) throws IOException { + this.buffer.putShort((short) s); + } + + @Override + public void writeChar(int c) throws IOException { + this.buffer.putChar((char) c); + } + + @Override + public void writeInt(int i) throws IOException { + this.buffer.putInt(i); + } + + @Override + public void writeVarInt(int i) throws IOException { + while((i & ~0x7F) != 0) { + this.writeByte((i & 0x7F) | 0x80); + i >>>= 7; + } + + this.writeByte(i); + } + + @Override + public void writeLong(long l) throws IOException { + this.buffer.putLong(l); + } + + @Override + public void writeVarLong(long l) throws IOException { + while((l & ~0x7F) != 0) { + this.writeByte((int) (l & 0x7F) | 0x80); + l >>>= 7; + } + + this.writeByte((int) l); + } + + @Override + public void writeFloat(float f) throws IOException { + this.buffer.putFloat(f); + } + + @Override + public void writeDouble(double d) throws IOException { + this.buffer.putDouble(d); + } + + @Override + public void writeBytes(byte b[]) throws IOException { + this.buffer.put(b); + } + + @Override + public void writeBytes(byte b[], int length) throws IOException { + this.buffer.put(b, 0, length); + } + + @Override + public void writeShorts(short[] s) throws IOException { + this.writeShorts(s, s.length); + } + + @Override + public void writeShorts(short[] s, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeShort(s[index]); + } + } + + @Override + public void writeInts(int[] i) throws IOException { + this.writeInts(i, i.length); + } + + @Override + public void writeInts(int[] i, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeInt(i[index]); + } + } + + @Override + public void writeLongs(long[] l) throws IOException { + this.writeLongs(l, l.length); + } + + @Override + public void writeLongs(long[] l, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeLong(l[index]); + } + } + + @Override + public void writeString(String s) throws IOException { + if(s == null) { + throw new IllegalArgumentException("String cannot be null!"); + } + + byte[] bytes = s.getBytes("UTF-8"); + if(bytes.length > 32767) { + throw new IOException("String too big (was " + s.length() + " bytes encoded, max " + 32767 + ")"); + } else { + this.writeVarInt(bytes.length); + this.writeBytes(bytes); + } + } + + @Override + public void writeUUID(UUID uuid) throws IOException { + this.writeLong(uuid.getMostSignificantBits()); + this.writeLong(uuid.getLeastSignificantBits()); + } + + @Override + public void flush() throws IOException { + } +} diff --git a/src/main/java/org/spacehq/packetlib/io/stream/StreamNetInput.java b/src/main/java/org/spacehq/packetlib/io/stream/StreamNetInput.java index 4740d7a5..69d1d1b4 100644 --- a/src/main/java/org/spacehq/packetlib/io/stream/StreamNetInput.java +++ b/src/main/java/org/spacehq/packetlib/io/stream/StreamNetInput.java @@ -146,6 +146,105 @@ public class StreamNetInput implements NetInput { return this.in.read(b, offset, length); } + @Override + public short[] readShorts(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + short s[] = new short[length]; + int read = this.readShorts(s); + if(read < length) { + throw new EOFException(); + } + + return s; + } + + @Override + public int readShorts(short[] s) throws IOException { + return this.readShorts(s, 0, s.length); + } + + @Override + public int readShorts(short[] s, int offset, int length) throws IOException { + for(int index = offset; index < offset + length; index++) { + try { + s[index] = this.readShort(); + } catch(EOFException e) { + return index - offset; + } + } + + return length; + } + + @Override + public int[] readInts(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + int i[] = new int[length]; + int read = this.readInts(i); + if(read < length) { + throw new EOFException(); + } + + return i; + } + + @Override + public int readInts(int[] i) throws IOException { + return this.readInts(i, 0, i.length); + } + + @Override + public int readInts(int[] i, int offset, int length) throws IOException { + for(int index = offset; index < offset + length; index++) { + try { + i[index] = this.readInt(); + } catch(EOFException e) { + return index - offset; + } + } + + return length; + } + + @Override + public long[] readLongs(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + long l[] = new long[length]; + int read = this.readLongs(l); + if(read < length) { + throw new EOFException(); + } + + return l; + } + + @Override + public int readLongs(long[] l) throws IOException { + return this.readLongs(l, 0, l.length); + } + + @Override + public int readLongs(long[] l, int offset, int length) throws IOException { + for(int index = offset; index < offset + length; index++) { + try { + l[index] = this.readLong(); + } catch(EOFException e) { + return index - offset; + } + } + + return length; + } + @Override public String readString() throws IOException { int length = this.readVarInt(); diff --git a/src/main/java/org/spacehq/packetlib/io/stream/StreamNetOutput.java b/src/main/java/org/spacehq/packetlib/io/stream/StreamNetOutput.java index 963212ce..29010791 100644 --- a/src/main/java/org/spacehq/packetlib/io/stream/StreamNetOutput.java +++ b/src/main/java/org/spacehq/packetlib/io/stream/StreamNetOutput.java @@ -103,6 +103,42 @@ public class StreamNetOutput implements NetOutput { this.out.write(b, 0, length); } + @Override + public void writeShorts(short[] s) throws IOException { + this.writeShorts(s, s.length); + } + + @Override + public void writeShorts(short[] s, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeShort(s[index]); + } + } + + @Override + public void writeInts(int[] i) throws IOException { + this.writeInts(i, i.length); + } + + @Override + public void writeInts(int[] i, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeInt(i[index]); + } + } + + @Override + public void writeLongs(long[] l) throws IOException { + this.writeLongs(l, l.length); + } + + @Override + public void writeLongs(long[] l, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeLong(l[index]); + } + } + @Override public void writeString(String s) throws IOException { if(s == null) { diff --git a/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetInput.java b/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetInput.java index 27223781..85e33ff9 100644 --- a/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetInput.java +++ b/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetInput.java @@ -127,6 +127,117 @@ public class ByteBufNetInput implements NetInput { return length; } + @Override + public short[] readShorts(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + short s[] = new short[length]; + for(int index = 0; index < length; index++) { + s[index] = this.readShort(); + } + + return s; + } + + @Override + public int readShorts(short[] s) throws IOException { + return this.readShorts(s, 0, s.length); + } + + @Override + public int readShorts(short[] s, int offset, int length) throws IOException { + int readable = this.buf.readableBytes(); + if(readable <= 0) { + return -1; + } + + if(readable < length * 2) { + length = readable / 2; + } + + for(int index = offset; index < offset + length; index++) { + s[index] = this.readShort(); + } + + return length; + } + + @Override + public int[] readInts(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + int i[] = new int[length]; + for(int index = 0; index < length; index++) { + i[index] = this.readInt(); + } + + return i; + } + + @Override + public int readInts(int[] i) throws IOException { + return this.readInts(i, 0, i.length); + } + + @Override + public int readInts(int[] i, int offset, int length) throws IOException { + int readable = this.buf.readableBytes(); + if(readable <= 0) { + return -1; + } + + if(readable < length * 4) { + length = readable / 4; + } + + for(int index = offset; index < offset + length; index++) { + i[index] = this.readInt(); + } + + return length; + } + + @Override + public long[] readLongs(int length) throws IOException { + if(length < 0) { + throw new IllegalArgumentException("Array cannot have length less than 0."); + } + + long l[] = new long[length]; + for(int index = 0; index < length; index++) { + l[index] = this.readLong(); + } + + return l; + } + + @Override + public int readLongs(long[] l) throws IOException { + return this.readLongs(l, 0, l.length); + } + + @Override + public int readLongs(long[] l, int offset, int length) throws IOException { + int readable = this.buf.readableBytes(); + if(readable <= 0) { + return -1; + } + + if(readable < length * 2) { + length = readable / 2; + } + + for(int index = offset; index < offset + length; index++) { + l[index] = this.readLong(); + } + + return length; + } + @Override public String readString() throws IOException { int length = this.readVarInt(); diff --git a/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetOutput.java b/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetOutput.java index 5c8390ae..cd245738 100644 --- a/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetOutput.java +++ b/src/main/java/org/spacehq/packetlib/tcp/io/ByteBufNetOutput.java @@ -86,6 +86,42 @@ public class ByteBufNetOutput implements NetOutput { this.buf.writeBytes(b, 0, length); } + @Override + public void writeShorts(short[] s) throws IOException { + this.writeShorts(s, s.length); + } + + @Override + public void writeShorts(short[] s, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeShort(s[index]); + } + } + + @Override + public void writeInts(int[] i) throws IOException { + this.writeInts(i, i.length); + } + + @Override + public void writeInts(int[] i, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeInt(i[index]); + } + } + + @Override + public void writeLongs(long[] l) throws IOException { + this.writeLongs(l, l.length); + } + + @Override + public void writeLongs(long[] l, int length) throws IOException { + for(int index = 0; index < length; index++) { + this.writeLong(l[index]); + } + } + @Override public void writeString(String s) throws IOException { if(s == null) {