initial commit
This commit is contained in:
44
src/main/java/ch/dissem/msgpack/Reader.java
Normal file
44
src/main/java/ch/dissem/msgpack/Reader.java
Normal file
@ -0,0 +1,44 @@
|
||||
package ch.dissem.msgpack;
|
||||
|
||||
import ch.dissem.msgpack.types.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reads MPType object from an {@link InputStream}.
|
||||
*/
|
||||
public class Reader {
|
||||
private List<MPType.Unpacker<?>> unpackers = new LinkedList<MPType.Unpacker<?>>();
|
||||
|
||||
public Reader() {
|
||||
unpackers.add(new MPNil.Unpacker());
|
||||
unpackers.add(new MPBoolean.Unpacker());
|
||||
unpackers.add(new MPInteger.Unpacker());
|
||||
unpackers.add(new MPFloat.Unpacker());
|
||||
unpackers.add(new MPDouble.Unpacker());
|
||||
unpackers.add(new MPString.Unpacker());
|
||||
unpackers.add(new MPBinary.Unpacker());
|
||||
unpackers.add(new MPMap.Unpacker(this));
|
||||
unpackers.add(new MPArray.Unpacker(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register your own extensions
|
||||
*/
|
||||
public void register(MPType.Unpacker<?> unpacker) {
|
||||
unpackers.add(unpacker);
|
||||
}
|
||||
|
||||
public MPType read(InputStream in) throws IOException {
|
||||
int firstByte = in.read();
|
||||
for (MPType.Unpacker<?> unpacker : unpackers) {
|
||||
if (unpacker.is(firstByte)) {
|
||||
return unpacker.unpack(firstByte, in);
|
||||
}
|
||||
}
|
||||
throw new IOException(String.format("Unsupported input, no reader for 0x%02x", firstByte));
|
||||
}
|
||||
}
|
101
src/main/java/ch/dissem/msgpack/types/MPArray.java
Normal file
101
src/main/java/ch/dissem/msgpack/types/MPArray.java
Normal file
@ -0,0 +1,101 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import ch.dissem.msgpack.Reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
public class MPArray<T extends MPType> implements MPType<List<T>> {
|
||||
private List<T> array;
|
||||
|
||||
public MPArray(List<T> array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public MPArray(T... objects) {
|
||||
this.array = Arrays.asList(objects);
|
||||
}
|
||||
|
||||
public List<T> getValue() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
int size = array.size();
|
||||
if (size < 16) {
|
||||
out.write(0b10010000 + size);
|
||||
} else if (size < 65536) {
|
||||
out.write(0xDC);
|
||||
out.write(ByteBuffer.allocate(2).putShort((short) size).array());
|
||||
} else {
|
||||
out.write(0xDD);
|
||||
out.write(ByteBuffer.allocate(4).putInt(size).array());
|
||||
}
|
||||
for (MPType o : array) {
|
||||
o.pack(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPArray<?> mpArray = (MPArray<?>) o;
|
||||
return Objects.equals(array, mpArray.array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append('[');
|
||||
Iterator<T> iterator = array.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
T item = iterator.next();
|
||||
result.append(item.toString());
|
||||
if (iterator.hasNext()) {
|
||||
result.append(", ");
|
||||
}
|
||||
}
|
||||
result.append(']');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPArray> {
|
||||
private final Reader reader;
|
||||
|
||||
public Unpacker(Reader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == 0xDC || firstByte == 0xDD || (firstByte & 0b11110000) == 0b10010000;
|
||||
}
|
||||
|
||||
public MPArray<MPType<?>> unpack(int firstByte, InputStream in) throws IOException {
|
||||
int size;
|
||||
if ((firstByte & 0b11110000) == 0b10010000) {
|
||||
size = firstByte & 0b00001111;
|
||||
} else if (firstByte == 0xDC) {
|
||||
size = in.read() << 8 | in.read();
|
||||
} else if (firstByte == 0xDD) {
|
||||
size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||
}
|
||||
List<MPType<?>> list = new LinkedList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MPType value = reader.read(in);
|
||||
list.add(value);
|
||||
}
|
||||
return new MPArray<>(list);
|
||||
}
|
||||
}
|
||||
}
|
74
src/main/java/ch/dissem/msgpack/types/MPBinary.java
Normal file
74
src/main/java/ch/dissem/msgpack/types/MPBinary.java
Normal file
@ -0,0 +1,74 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||
|
||||
public class MPBinary implements MPType<byte[]> {
|
||||
private byte[] value;
|
||||
|
||||
public MPBinary(byte[] value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public byte[] getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
int size = value.length;
|
||||
if (size < 256) {
|
||||
out.write(0xC4);
|
||||
out.write((byte) size);
|
||||
} else if (size < 65536) {
|
||||
out.write(0xC5);
|
||||
out.write(ByteBuffer.allocate(2).putShort((short) size).array());
|
||||
} else {
|
||||
out.write(0xC6);
|
||||
out.write(ByteBuffer.allocate(4).putInt(size).array());
|
||||
}
|
||||
out.write(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPBinary mpBinary = (MPBinary) o;
|
||||
return Arrays.equals(value, mpBinary.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return null; // TODO base64
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPBinary> {
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == 0xC4 || firstByte == 0xC5 || firstByte == 0xC6;
|
||||
}
|
||||
|
||||
public MPBinary unpack(int firstByte, InputStream in) throws IOException {
|
||||
int size;
|
||||
if (firstByte == 0xC4) {
|
||||
size = in.read() << 8 | in.read();
|
||||
} else if (firstByte == 0xC5) {
|
||||
size = in.read() << 8 | in.read();
|
||||
} else if (firstByte == 0xC6) {
|
||||
size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||
}
|
||||
return new MPBinary(bytes(in, size).array());
|
||||
}
|
||||
}
|
||||
}
|
64
src/main/java/ch/dissem/msgpack/types/MPBoolean.java
Normal file
64
src/main/java/ch/dissem/msgpack/types/MPBoolean.java
Normal file
@ -0,0 +1,64 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MPBoolean implements MPType<Boolean> {
|
||||
private final static int FALSE = 0xC2;
|
||||
private final static int TRUE = 0xC3;
|
||||
|
||||
private final boolean value;
|
||||
|
||||
public MPBoolean(boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
if (value) {
|
||||
out.write(TRUE);
|
||||
} else {
|
||||
out.write(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPBoolean mpBoolean = (MPBoolean) o;
|
||||
return value == mpBoolean.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPBoolean> {
|
||||
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == TRUE || firstByte == FALSE;
|
||||
}
|
||||
|
||||
public MPBoolean unpack(int firstByte, InputStream in) {
|
||||
if (firstByte == TRUE) {
|
||||
return new MPBoolean(true);
|
||||
} else if (firstByte == FALSE) {
|
||||
return new MPBoolean(false);
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("0xC2 or 0xC3 expected but was 0x%02x", firstByte));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
src/main/java/ch/dissem/msgpack/types/MPDouble.java
Normal file
59
src/main/java/ch/dissem/msgpack/types/MPDouble.java
Normal file
@ -0,0 +1,59 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||
|
||||
public class MPDouble implements MPType<Double> {
|
||||
private double value;
|
||||
|
||||
public MPDouble(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
out.write(0xCB);
|
||||
out.write(ByteBuffer.allocate(8).putDouble(value).array());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPDouble mpDouble = (MPDouble) o;
|
||||
return Double.compare(mpDouble.value, value) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPDouble> {
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == 0xCB;
|
||||
}
|
||||
|
||||
public MPDouble unpack(int firstByte, InputStream in) throws IOException {
|
||||
if (firstByte == 0xCB) {
|
||||
return new MPDouble(bytes(in, 8).getDouble());
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
src/main/java/ch/dissem/msgpack/types/MPFloat.java
Normal file
59
src/main/java/ch/dissem/msgpack/types/MPFloat.java
Normal file
@ -0,0 +1,59 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||
|
||||
public class MPFloat implements MPType<Float> {
|
||||
private float value;
|
||||
|
||||
public MPFloat(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
out.write(0xCA);
|
||||
out.write(ByteBuffer.allocate(4).putFloat(value).array());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPFloat mpFloat = (MPFloat) o;
|
||||
return Float.compare(mpFloat.value, value) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPFloat> {
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == 0xCA;
|
||||
}
|
||||
|
||||
public MPFloat unpack(int firstByte, InputStream in) throws IOException {
|
||||
if (firstByte == 0xCA) {
|
||||
return new MPFloat(bytes(in, 4).getFloat());
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
124
src/main/java/ch/dissem/msgpack/types/MPInteger.java
Normal file
124
src/main/java/ch/dissem/msgpack/types/MPInteger.java
Normal file
@ -0,0 +1,124 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||
|
||||
public class MPInteger implements MPType<Long> {
|
||||
private long value;
|
||||
|
||||
public MPInteger(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
if ((value > ((byte) 0b11100000) && value < 0x80)) {
|
||||
out.write((int) value);
|
||||
} else if (value > 0) {
|
||||
if (value <= 0xFF) {
|
||||
out.write(0xCC);
|
||||
out.write((int) value);
|
||||
} else if (value <= 0xFFFF) {
|
||||
out.write(0xCD);
|
||||
out.write(ByteBuffer.allocate(2).putShort((short) value).array());
|
||||
} else if (value < 0xFFFFFFFFL) {
|
||||
out.write(0xCE);
|
||||
out.write(ByteBuffer.allocate(4).putInt((int) value).array());
|
||||
} else {
|
||||
out.write(0xCF);
|
||||
out.write(ByteBuffer.allocate(8).putLong(value).array());
|
||||
}
|
||||
} else {
|
||||
if (value >= Byte.MIN_VALUE) {
|
||||
out.write(0xD0);
|
||||
out.write(ByteBuffer.allocate(1).put((byte) value).array());
|
||||
} else if (value >= Short.MIN_VALUE) {
|
||||
out.write(0xD1);
|
||||
out.write(ByteBuffer.allocate(2).putShort((short) value).array());
|
||||
} else if (value >= Integer.MIN_VALUE) {
|
||||
out.write(0xD2);
|
||||
out.write(ByteBuffer.allocate(4).putInt((int) value).array());
|
||||
} else {
|
||||
out.write(0xD3);
|
||||
out.write(ByteBuffer.allocate(8).putLong(value).array());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPInteger mpInteger = (MPInteger) o;
|
||||
return value == mpInteger.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPInteger> {
|
||||
public boolean is(int firstByte) {
|
||||
switch (firstByte) {
|
||||
case 0xCC:
|
||||
case 0xCD:
|
||||
case 0xCE:
|
||||
case 0xCF:
|
||||
case 0xD0:
|
||||
case 0xD1:
|
||||
case 0xD2:
|
||||
case 0xD3:
|
||||
return true;
|
||||
default:
|
||||
return (firstByte & 0b10000000) == 0 || (firstByte & 0b11100000) == 0b11100000;
|
||||
}
|
||||
}
|
||||
|
||||
public MPInteger unpack(int firstByte, InputStream in) throws IOException {
|
||||
if ((firstByte & 0b10000000) == 0 || (firstByte & 0b11100000) == 0b11100000) {
|
||||
return new MPInteger((byte) firstByte);
|
||||
} else {
|
||||
switch (firstByte) {
|
||||
case 0xCC:
|
||||
return new MPInteger(in.read());
|
||||
case 0xCD:
|
||||
return new MPInteger(in.read() << 8 | in.read());
|
||||
case 0xCE:
|
||||
return new MPInteger(in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read());
|
||||
case 0xCF: {
|
||||
long value = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
value = value << 8 | in.read();
|
||||
}
|
||||
return new MPInteger(value);
|
||||
}
|
||||
case 0xD0:
|
||||
return new MPInteger(bytes(in, 1).get());
|
||||
case 0xD1:
|
||||
return new MPInteger(bytes(in, 2).getShort());
|
||||
case 0xD2:
|
||||
return new MPInteger(bytes(in, 4).getInt());
|
||||
case 0xD3:
|
||||
return new MPInteger(bytes(in, 8).getLong());
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
105
src/main/java/ch/dissem/msgpack/types/MPMap.java
Normal file
105
src/main/java/ch/dissem/msgpack/types/MPMap.java
Normal file
@ -0,0 +1,105 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import ch.dissem.msgpack.Reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MPMap<K extends MPType, V extends MPType> implements MPType<Map<K, V>> {
|
||||
private Map<K, V> map;
|
||||
|
||||
public MPMap(Map<K, V> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> getValue() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
int size = map.size();
|
||||
if (size < 16) {
|
||||
out.write(0x80 + size);
|
||||
} else if (size < 65536) {
|
||||
out.write(0xDE);
|
||||
out.write(ByteBuffer.allocate(2).putShort((short) size).array());
|
||||
} else {
|
||||
out.write(0xDF);
|
||||
out.write(ByteBuffer.allocate(4).putInt(size).array());
|
||||
}
|
||||
for (Map.Entry<K, V> e : map.entrySet()) {
|
||||
e.getKey().pack(out);
|
||||
e.getValue().pack(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPMap<?, ?> mpMap = (MPMap<?, ?>) o;
|
||||
return Objects.equals(map, mpMap.map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append('{');
|
||||
Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<K, V> item = iterator.next();
|
||||
result.append(item.getKey().toString());
|
||||
result.append(": ");
|
||||
result.append(item.getValue().toString());
|
||||
if (iterator.hasNext()) {
|
||||
result.append(", ");
|
||||
}
|
||||
}
|
||||
result.append('}');
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPMap> {
|
||||
private final Reader reader;
|
||||
|
||||
public Unpacker(Reader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == 0xDE || firstByte == 0xDF || (firstByte & 0xF0) == 0x80;
|
||||
}
|
||||
|
||||
public MPMap<MPType<?>, MPType<?>> unpack(int firstByte, InputStream in) throws IOException {
|
||||
int size;
|
||||
if ((firstByte & 0xF0) == 0x80) {
|
||||
size = firstByte & 0x0F;
|
||||
} else if (firstByte == 0xDE) {
|
||||
size = in.read() << 8 | in.read();
|
||||
} else if (firstByte == 0xDF) {
|
||||
size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||
}
|
||||
Map<MPType<?>, MPType<?>> map = new LinkedHashMap<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MPType key = reader.read(in);
|
||||
MPType value = reader.read(in);
|
||||
map.put(key, value);
|
||||
}
|
||||
return new MPMap<>(map);
|
||||
}
|
||||
}
|
||||
}
|
46
src/main/java/ch/dissem/msgpack/types/MPNil.java
Normal file
46
src/main/java/ch/dissem/msgpack/types/MPNil.java
Normal file
@ -0,0 +1,46 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class MPNil implements MPType<Void> {
|
||||
private final static int NIL = 0xC0;
|
||||
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
out.write(NIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof MPNil;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "nil";
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPNil> {
|
||||
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == NIL;
|
||||
}
|
||||
|
||||
public MPNil unpack(int firstByte, InputStream in) {
|
||||
if (firstByte != NIL) {
|
||||
throw new IllegalArgumentException(String.format("0xC0 expected but was 0x%02x", firstByte));
|
||||
}
|
||||
return new MPNil();
|
||||
}
|
||||
}
|
||||
}
|
78
src/main/java/ch/dissem/msgpack/types/MPString.java
Normal file
78
src/main/java/ch/dissem/msgpack/types/MPString.java
Normal file
@ -0,0 +1,78 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||
|
||||
public class MPString implements MPType<String> {
|
||||
private String value;
|
||||
|
||||
public MPString(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void pack(OutputStream out) throws IOException {
|
||||
int size = value.length();
|
||||
if (size < 16) {
|
||||
out.write(0b10100000 + size);
|
||||
} else if (size < 256) {
|
||||
out.write(0xD9);
|
||||
out.write((byte) size);
|
||||
} else if (size < 65536) {
|
||||
out.write(0xDA);
|
||||
out.write(ByteBuffer.allocate(2).putShort((short) size).array());
|
||||
} else {
|
||||
out.write(0xDB);
|
||||
out.write(ByteBuffer.allocate(4).putInt(size).array());
|
||||
}
|
||||
out.write(value.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MPString mpString = (MPString) o;
|
||||
return Objects.equals(value, mpString.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return '"' + value + '"'; // FIXME: escape value
|
||||
}
|
||||
|
||||
public static class Unpacker implements MPType.Unpacker<MPString> {
|
||||
public boolean is(int firstByte) {
|
||||
return firstByte == 0xD9 || firstByte == 0xDA || firstByte == 0xDB || (firstByte & 0b11100000) == 0b10100000;
|
||||
}
|
||||
|
||||
public MPString unpack(int firstByte, InputStream in) throws IOException {
|
||||
int size;
|
||||
if ((firstByte & 0b11100000) == 0b10100000) {
|
||||
size = firstByte & 0b00011111;
|
||||
} else if (firstByte == 0xD9) {
|
||||
size = in.read() << 8 | in.read();
|
||||
} else if (firstByte == 0xDA) {
|
||||
size = in.read() << 8 | in.read();
|
||||
} else if (firstByte == 0xDB) {
|
||||
size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||
}
|
||||
return new MPString(new String(bytes(in, size).array(), "UTF-8"));
|
||||
}
|
||||
}
|
||||
}
|
20
src/main/java/ch/dissem/msgpack/types/MPType.java
Normal file
20
src/main/java/ch/dissem/msgpack/types/MPType.java
Normal file
@ -0,0 +1,20 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Representation for some msgpack encoded data.
|
||||
*/
|
||||
public interface MPType<T> {
|
||||
interface Unpacker<M extends MPType> {
|
||||
boolean is(int firstByte);
|
||||
|
||||
M unpack(int firstByte, InputStream in) throws IOException;
|
||||
}
|
||||
|
||||
T getValue();
|
||||
|
||||
void pack(OutputStream out) throws IOException;
|
||||
}
|
20
src/main/java/ch/dissem/msgpack/types/Utils.java
Normal file
20
src/main/java/ch/dissem/msgpack/types/Utils.java
Normal file
@ -0,0 +1,20 @@
|
||||
package ch.dissem.msgpack.types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
class Utils {
|
||||
static ByteBuffer bytes(InputStream in, int count) throws IOException {
|
||||
byte[] result = new byte[count];
|
||||
int off = 0;
|
||||
while (off < count) {
|
||||
int read = in.read(result, off, count - off);
|
||||
if (read < 0) {
|
||||
throw new IOException("Unexpected end of stream, wanted to read " + count + " bytes but only got " + off);
|
||||
}
|
||||
off += read;
|
||||
}
|
||||
return ByteBuffer.wrap(result);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user