Added comments, implemented interfaces for some added convenience.
This commit is contained in:
parent
4b79e232d0
commit
30da667fdb
@ -8,9 +8,19 @@ import java.io.OutputStream;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class MPArray<T extends MPType> implements MPType<List<T>> {
|
/**
|
||||||
|
* Representation of a msgpack encoded array. Uses a list to represent data internally, and implements the {@link List}
|
||||||
|
* interface for your convenience.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public class MPArray<T extends MPType> implements MPType<List<T>>, List<T> {
|
||||||
private List<T> array;
|
private List<T> array;
|
||||||
|
|
||||||
|
public MPArray() {
|
||||||
|
this.array = new LinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
public MPArray(List<T> array) {
|
public MPArray(List<T> array) {
|
||||||
this.array = array;
|
this.array = array;
|
||||||
}
|
}
|
||||||
@ -39,6 +49,76 @@ public class MPArray<T extends MPType> implements MPType<List<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return array.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return array.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return array.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return array.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return array.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T1> T1[] toArray(T1[] t1s) {
|
||||||
|
return array.toArray(t1s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(T t) {
|
||||||
|
return array.add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
return array.remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> collection) {
|
||||||
|
return array.containsAll(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends T> collection) {
|
||||||
|
return array.addAll(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int i, Collection<? extends T> collection) {
|
||||||
|
return array.addAll(i, collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> collection) {
|
||||||
|
return array.removeAll(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> collection) {
|
||||||
|
return array.retainAll(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
array.clear();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
@ -52,6 +132,51 @@ public class MPArray<T extends MPType> implements MPType<List<T>> {
|
|||||||
return Objects.hash(array);
|
return Objects.hash(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(int i) {
|
||||||
|
return array.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T set(int i, T t) {
|
||||||
|
return array.set(i, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int i, T t) {
|
||||||
|
array.add(i, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T remove(int i) {
|
||||||
|
return array.remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
return array.indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
return array.lastIndexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator<T> listIterator() {
|
||||||
|
return array.listIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator<T> listIterator(int i) {
|
||||||
|
return array.listIterator(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> subList(int fromIndex, int toIndex) {
|
||||||
|
return array.subList(fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
|
@ -8,6 +8,9 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of msgpack encoded binary data a.k.a. byte array.
|
||||||
|
*/
|
||||||
public class MPBinary implements MPType<byte[]> {
|
public class MPBinary implements MPType<byte[]> {
|
||||||
private byte[] value;
|
private byte[] value;
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a msgpack encoded boolean.
|
||||||
|
*/
|
||||||
public class MPBoolean implements MPType<Boolean> {
|
public class MPBoolean implements MPType<Boolean> {
|
||||||
private final static int FALSE = 0xC2;
|
private final static int FALSE = 0xC2;
|
||||||
private final static int TRUE = 0xC3;
|
private final static int TRUE = 0xC3;
|
||||||
|
@ -8,6 +8,9 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a msgpack encoded float64 number.
|
||||||
|
*/
|
||||||
public class MPDouble implements MPType<Double> {
|
public class MPDouble implements MPType<Double> {
|
||||||
private double value;
|
private double value;
|
||||||
|
|
||||||
|
@ -8,6 +8,9 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a msgpack encoded float32 number.
|
||||||
|
*/
|
||||||
public class MPFloat implements MPType<Float> {
|
public class MPFloat implements MPType<Float> {
|
||||||
private float value;
|
private float value;
|
||||||
|
|
||||||
|
@ -8,6 +8,12 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a msgpack encoded integer. The encoding is automatically selected according to the value's size.
|
||||||
|
* Uses long due to the fact that the msgpack integer implementation may contain up to 64 bit numbers, corresponding
|
||||||
|
* to Java long values. Also note that uint64 values may be too large for signed long (thanks Java for not supporting
|
||||||
|
* unsigned values) and end in a negative value.
|
||||||
|
*/
|
||||||
public class MPInteger implements MPType<Long> {
|
public class MPInteger implements MPType<Long> {
|
||||||
private long value;
|
private long value;
|
||||||
|
|
||||||
|
@ -6,14 +6,22 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Iterator;
|
import java.util.*;
|
||||||
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>> {
|
/**
|
||||||
|
* Representation of a msgpack encoded map. It is recommended to use a {@link LinkedHashMap} to ensure the order
|
||||||
|
* of entries. For convenience, it also implements the {@link Map} interface.
|
||||||
|
*
|
||||||
|
* @param <K>
|
||||||
|
* @param <V>
|
||||||
|
*/
|
||||||
|
public class MPMap<K extends MPType, V extends MPType> implements MPType<Map<K, V>>, Map<K, V> {
|
||||||
private Map<K, V> map;
|
private Map<K, V> map;
|
||||||
|
|
||||||
|
public MPMap() {
|
||||||
|
this.map = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
public MPMap(Map<K, V> map) {
|
public MPMap(Map<K, V> map) {
|
||||||
this.map = map;
|
this.map = map;
|
||||||
}
|
}
|
||||||
@ -40,6 +48,66 @@ public class MPMap<K extends MPType, V extends MPType> implements MPType<Map<K,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return map.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object o) {
|
||||||
|
return map.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object o) {
|
||||||
|
return map.containsValue(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object o) {
|
||||||
|
return map.get(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(K k, V v) {
|
||||||
|
return map.put(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object o) {
|
||||||
|
return map.remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> map) {
|
||||||
|
this.map.putAll(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<K> keySet() {
|
||||||
|
return map.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
return map.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<K, V>> entrySet() {
|
||||||
|
return map.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -4,6 +4,9 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of msgpack encoded nil / null.
|
||||||
|
*/
|
||||||
public class MPNil implements MPType<Void> {
|
public class MPNil implements MPType<Void> {
|
||||||
private final static int NIL = 0xC0;
|
private final static int NIL = 0xC0;
|
||||||
|
|
||||||
@ -27,7 +30,7 @@ public class MPNil implements MPType<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "nil";
|
return "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Unpacker implements MPType.Unpacker<MPNil> {
|
public static class Unpacker implements MPType.Unpacker<MPNil> {
|
||||||
|
@ -4,12 +4,42 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static ch.dissem.msgpack.types.Utils.bytes;
|
import static ch.dissem.msgpack.types.Utils.bytes;
|
||||||
|
|
||||||
public class MPString implements MPType<String> {
|
/**
|
||||||
private String value;
|
* Representation of a msgpack encoded string. The encoding is automatically selected according to the string's length.
|
||||||
|
* <p>
|
||||||
|
* The default encoding is UTF-8.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class MPString implements MPType<String>, CharSequence {
|
||||||
|
private static final int FIXSTR_PREFIX = 0b10100000;
|
||||||
|
private static final int FIXSTR_PREFIX_FILTER = 0b11100000;
|
||||||
|
private static final int STR8_PREFIX = 0xD9;
|
||||||
|
private static final int STR8_LIMIT = 256;
|
||||||
|
private static final int STR16_PREFIX = 0xDA;
|
||||||
|
private static final int STR16_LIMIT = 65536;
|
||||||
|
private static final int STR32_PREFIX = 0xDB;
|
||||||
|
private static final int FIXSTR_FILTER = 0b00011111;
|
||||||
|
|
||||||
|
private static Charset encoding = Charset.forName("UTF-8");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method if for some messed up reason you really need to use something else than UTF-8.
|
||||||
|
* Ask yourself: why should I? Is this really necessary?
|
||||||
|
* <p>
|
||||||
|
* It will set the encoding for all {@link MPString}s, but if you have inconsistent encoding in your
|
||||||
|
* format you're lost anyway.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public static void setEncoding(Charset encoding) {
|
||||||
|
MPString.encoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
public MPString(String value) {
|
public MPString(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -22,18 +52,18 @@ public class MPString implements MPType<String> {
|
|||||||
public void pack(OutputStream out) throws IOException {
|
public void pack(OutputStream out) throws IOException {
|
||||||
int size = value.length();
|
int size = value.length();
|
||||||
if (size < 32) {
|
if (size < 32) {
|
||||||
out.write(0b10100000 + size);
|
out.write(FIXSTR_PREFIX + size);
|
||||||
} else if (size < 256) {
|
} else if (size < STR8_LIMIT) {
|
||||||
out.write(0xD9);
|
out.write(STR8_PREFIX);
|
||||||
out.write(size);
|
out.write(size);
|
||||||
} else if (size < 65536) {
|
} else if (size < STR16_LIMIT) {
|
||||||
out.write(0xDA);
|
out.write(STR16_PREFIX);
|
||||||
out.write(ByteBuffer.allocate(2).putShort((short) size).array());
|
out.write(ByteBuffer.allocate(2).putShort((short) size).array());
|
||||||
} else {
|
} else {
|
||||||
out.write(0xDB);
|
out.write(STR32_PREFIX);
|
||||||
out.write(ByteBuffer.allocate(4).putInt(size).array());
|
out.write(ByteBuffer.allocate(4).putInt(size).array());
|
||||||
}
|
}
|
||||||
out.write(value.getBytes("UTF-8"));
|
out.write(value.getBytes(encoding));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,30 +79,46 @@ public class MPString implements MPType<String> {
|
|||||||
return Objects.hash(value);
|
return Objects.hash(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int length() {
|
||||||
|
return value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char charAt(int i) {
|
||||||
|
return value.charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence subSequence(int beginIndex, int endIndex) {
|
||||||
|
return value.subSequence(beginIndex, endIndex);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return '"' + value + '"'; // FIXME: escape value
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Unpacker implements MPType.Unpacker<MPString> {
|
public static class Unpacker implements MPType.Unpacker<MPString> {
|
||||||
public boolean is(int firstByte) {
|
public boolean is(int firstByte) {
|
||||||
return firstByte == 0xD9 || firstByte == 0xDA || firstByte == 0xDB || (firstByte & 0b11100000) == 0b10100000;
|
return firstByte == STR8_PREFIX || firstByte == STR16_PREFIX || firstByte == STR32_PREFIX
|
||||||
|
|| (firstByte & FIXSTR_PREFIX_FILTER) == FIXSTR_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPString unpack(int firstByte, InputStream in) throws IOException {
|
public MPString unpack(int firstByte, InputStream in) throws IOException {
|
||||||
int size;
|
int size;
|
||||||
if ((firstByte & 0b11100000) == 0b10100000) {
|
if ((firstByte & FIXSTR_PREFIX_FILTER) == FIXSTR_PREFIX) {
|
||||||
size = firstByte & 0b00011111;
|
size = firstByte & FIXSTR_FILTER;
|
||||||
} else if (firstByte == 0xD9) {
|
} else if (firstByte == STR8_PREFIX) {
|
||||||
size = in.read();
|
size = in.read();
|
||||||
} else if (firstByte == 0xDA) {
|
} else if (firstByte == STR16_PREFIX) {
|
||||||
size = in.read() << 8 | in.read();
|
size = in.read() << 8 | in.read();
|
||||||
} else if (firstByte == 0xDB) {
|
} else if (firstByte == STR32_PREFIX) {
|
||||||
size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
|
size = in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
throw new IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte));
|
||||||
}
|
}
|
||||||
return new MPString(new String(bytes(in, size).array(), "UTF-8"));
|
return new MPString(new String(bytes(in, size).array(), encoding));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation for some msgpack encoded data.
|
* Representation of some msgpack encoded data.
|
||||||
*/
|
*/
|
||||||
public interface MPType<T> {
|
public interface MPType<T> {
|
||||||
interface Unpacker<M extends MPType> {
|
interface Unpacker<M extends MPType> {
|
||||||
|
@ -5,6 +5,9 @@ import java.io.InputStream;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
|
/**
|
||||||
|
* Returns a {@link ByteBuffer} containing the next <code>count</code> bytes from the {@link InputStream}.
|
||||||
|
*/
|
||||||
static ByteBuffer bytes(InputStream in, int count) throws IOException {
|
static ByteBuffer bytes(InputStream in, int count) throws IOException {
|
||||||
byte[] result = new byte[count];
|
byte[] result = new byte[count];
|
||||||
int off = 0;
|
int off = 0;
|
||||||
|
@ -29,9 +29,9 @@ public class ReaderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ensureDemoJsonIsEncodedCorrectly() throws Exception {
|
public void ensureDemoJsonIsEncodedCorrectly() throws Exception {
|
||||||
MPMap<MPString, MPType<?>> object = new MPMap<>(new LinkedHashMap<MPString, MPType<?>>());
|
MPMap<MPString, MPType<?>> object = new MPMap<>();
|
||||||
object.getValue().put(new MPString("compact"), new MPBoolean(true));
|
object.put(new MPString("compact"), new MPBoolean(true));
|
||||||
object.getValue().put(new MPString("schema"), new MPInteger(0));
|
object.put(new MPString("schema"), new MPInteger(0));
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
object.pack(out);
|
object.pack(out);
|
||||||
assertThat(out.toByteArray(), is(bytes("demo.mp")));
|
assertThat(out.toByteArray(), is(bytes("demo.mp")));
|
||||||
|
Loading…
Reference in New Issue
Block a user