Alon Albert
7 years ago
6 changed files with 1 additions and 1051 deletions
@ -1,32 +0,0 @@ |
|||||||
package se.dimovski.rencode; |
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream; |
|
||||||
import java.io.ByteArrayOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
|
|
||||||
public class Rencode |
|
||||||
{ |
|
||||||
|
|
||||||
public static Object decode(byte[] data) throws IOException |
|
||||||
{ |
|
||||||
final InputStream is = new ByteArrayInputStream(data); |
|
||||||
final RencodeInputStream inputStream = new RencodeInputStream(is); |
|
||||||
|
|
||||||
final Object decoded = inputStream.readObject(); |
|
||||||
inputStream.close(); |
|
||||||
|
|
||||||
return decoded; |
|
||||||
} |
|
||||||
|
|
||||||
public static byte[] encode(Object obj) throws IOException |
|
||||||
{ |
|
||||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
||||||
final RencodeOutputStream output = new RencodeOutputStream(baos); |
|
||||||
output.writeObject(obj); |
|
||||||
final byte[] encoded = baos.toByteArray(); |
|
||||||
output.close(); |
|
||||||
return encoded; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,494 +0,0 @@ |
|||||||
package se.dimovski.rencode; |
|
||||||
|
|
||||||
import java.io.DataInput; |
|
||||||
import java.io.EOFException; |
|
||||||
import java.io.FilterInputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.io.UnsupportedEncodingException; |
|
||||||
import java.math.BigDecimal; |
|
||||||
import java.math.BigInteger; |
|
||||||
import java.nio.ByteBuffer; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.TreeMap; |
|
||||||
|
|
||||||
public class RencodeInputStream extends FilterInputStream implements DataInput |
|
||||||
{ |
|
||||||
/** |
|
||||||
* The charset that is being used for {@link String}s. |
|
||||||
*/ |
|
||||||
private final String charset; |
|
||||||
|
|
||||||
/** |
|
||||||
* Whether or not all byte-Arrays should be decoded as {@link String}s. |
|
||||||
*/ |
|
||||||
private final boolean decodeAsString; |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a {@link RencodeInputStream} with the default encoding. |
|
||||||
*/ |
|
||||||
public RencodeInputStream(InputStream in) |
|
||||||
{ |
|
||||||
this(in, Utils.UTF_8, false); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a {@link RencodeInputStream} with the given encoding. |
|
||||||
*/ |
|
||||||
public RencodeInputStream(InputStream in, String charset) |
|
||||||
{ |
|
||||||
this(in, charset, false); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a {@link RencodeInputStream} with the default encoding. |
|
||||||
*/ |
|
||||||
public RencodeInputStream(InputStream in, boolean decodeAsString) |
|
||||||
{ |
|
||||||
this(in, Utils.UTF_8, decodeAsString); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a {@link RencodeInputStream} with the given encoding. |
|
||||||
*/ |
|
||||||
public RencodeInputStream(InputStream in, String charset, boolean decodeAsString) |
|
||||||
{ |
|
||||||
super(in); |
|
||||||
|
|
||||||
if (charset == null) |
|
||||||
{ |
|
||||||
throw new IllegalArgumentException("charset is null"); |
|
||||||
} |
|
||||||
|
|
||||||
this.charset = charset; |
|
||||||
this.decodeAsString = decodeAsString; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the charset that is used to decode {@link String}s. The default |
|
||||||
* value is UTF-8. |
|
||||||
*/ |
|
||||||
public String getCharset() |
|
||||||
{ |
|
||||||
return charset; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns true if all byte-Arrays are being turned into {@link String}s. |
|
||||||
*/ |
|
||||||
public boolean isDecodeAsString() |
|
||||||
{ |
|
||||||
return decodeAsString; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns an {@link Object}. |
|
||||||
*/ |
|
||||||
public Object readObject() throws IOException |
|
||||||
{ |
|
||||||
int token = readToken(); |
|
||||||
|
|
||||||
return readObject(token); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns an {@link Object}. |
|
||||||
*/ |
|
||||||
protected Object readObject(int token) throws IOException |
|
||||||
{ |
|
||||||
if (token == TypeCode.DICTIONARY) |
|
||||||
{ |
|
||||||
return readMap0(Object.class); |
|
||||||
} |
|
||||||
else if (Utils.isFixedDictionary(token)) |
|
||||||
{ |
|
||||||
return readMap0(Object.class, token); |
|
||||||
} |
|
||||||
else if (token == TypeCode.LIST) |
|
||||||
{ |
|
||||||
return readList0(Object.class); |
|
||||||
} |
|
||||||
else if (Utils.isFixedList(token)) |
|
||||||
{ |
|
||||||
return readList0(Object.class, token); |
|
||||||
} |
|
||||||
else if (Utils.isNumber(token)) |
|
||||||
{ |
|
||||||
return readNumber0(token); |
|
||||||
} |
|
||||||
else if (token == TypeCode.FALSE || token == TypeCode.TRUE) |
|
||||||
{ |
|
||||||
return readBoolean0(token); |
|
||||||
} |
|
||||||
else if (token == TypeCode.NULL) |
|
||||||
{ |
|
||||||
return null; |
|
||||||
} |
|
||||||
else if (Utils.isDigit(token) || Utils.isFixedString(token)) |
|
||||||
{ |
|
||||||
return readString(token, charset); |
|
||||||
} |
|
||||||
|
|
||||||
throw new IOException("Not implemented: " + token); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns a {@link Map}. |
|
||||||
*/ |
|
||||||
public Map<String, ?> readMap() throws IOException |
|
||||||
{ |
|
||||||
return readMap(Object.class); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns a {@link Map}. |
|
||||||
*/ |
|
||||||
public <T> Map<String, T> readMap(Class<T> clazz) throws IOException |
|
||||||
{ |
|
||||||
int token = readToken(); |
|
||||||
|
|
||||||
if (token != TypeCode.DICTIONARY) |
|
||||||
{ |
|
||||||
throw new IOException(); |
|
||||||
} |
|
||||||
|
|
||||||
return readMap0(clazz); |
|
||||||
} |
|
||||||
|
|
||||||
private <T> Map<String, T> readMap0(Class<T> clazz) throws IOException |
|
||||||
{ |
|
||||||
Map<String, T> map = new TreeMap<String, T>(); |
|
||||||
int token = -1; |
|
||||||
while ((token = readToken()) != TypeCode.END) |
|
||||||
{ |
|
||||||
readMapItem(clazz, token, map); |
|
||||||
} |
|
||||||
|
|
||||||
return map; |
|
||||||
} |
|
||||||
|
|
||||||
private <T> Map<String, T> readMap0(Class<T> clazz, int token) throws IOException |
|
||||||
{ |
|
||||||
Map<String, T> map = new TreeMap<String, T>(); |
|
||||||
|
|
||||||
int count = token - TypeCode.EMBEDDED.DICT_START; |
|
||||||
for (int i = 0; i < count; i++) |
|
||||||
{ |
|
||||||
readMapItem(clazz, readToken(), map); |
|
||||||
} |
|
||||||
|
|
||||||
return map; |
|
||||||
} |
|
||||||
|
|
||||||
private <T> void readMapItem(Class<T> clazz, int token, Map<String, T> map) throws UnsupportedEncodingException, |
|
||||||
IOException |
|
||||||
{ |
|
||||||
String key = readString(token, charset); |
|
||||||
T value = clazz.cast(readObject()); |
|
||||||
|
|
||||||
map.put(key, value); |
|
||||||
} |
|
||||||
|
|
||||||
public int readToken() throws IOException |
|
||||||
{ |
|
||||||
int token = super.read(); |
|
||||||
if (token == -1) |
|
||||||
{ |
|
||||||
throw new EOFException(); |
|
||||||
} |
|
||||||
return token; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns a {@link List}. |
|
||||||
*/ |
|
||||||
public List<?> readList() throws IOException |
|
||||||
{ |
|
||||||
return readList(Object.class); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns a {@link List}. |
|
||||||
*/ |
|
||||||
public <T> List<T> readList(Class<T> clazz) throws IOException |
|
||||||
{ |
|
||||||
int token = readToken(); |
|
||||||
|
|
||||||
if (token != TypeCode.LIST) |
|
||||||
{ |
|
||||||
throw new IOException(); |
|
||||||
} |
|
||||||
|
|
||||||
return readList0(clazz); |
|
||||||
} |
|
||||||
|
|
||||||
private <T> List<T> readList0(Class<T> clazz) throws IOException |
|
||||||
{ |
|
||||||
List<T> list = new ArrayList<T>(); |
|
||||||
int token = -1; |
|
||||||
while ((token = readToken()) != TypeCode.END) |
|
||||||
{ |
|
||||||
list.add(clazz.cast(readObject(token))); |
|
||||||
} |
|
||||||
return list; |
|
||||||
} |
|
||||||
|
|
||||||
private <T> List<T> readList0(Class<T> clazz, int token) throws IOException |
|
||||||
{ |
|
||||||
List<T> list = new ArrayList<T>(); |
|
||||||
int length = token - TypeCode.EMBEDDED.LIST_START; |
|
||||||
for (int i = 0; i < length; i++) |
|
||||||
{ |
|
||||||
list.add(clazz.cast(readObject())); |
|
||||||
} |
|
||||||
return list; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean readBoolean() throws IOException |
|
||||||
{ |
|
||||||
return readBoolean0(readToken()); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean readBoolean0(int token) throws IOException |
|
||||||
{ |
|
||||||
if (token == TypeCode.FALSE) |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
else if (token == TypeCode.TRUE) |
|
||||||
{ |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
throw new IOException(); |
|
||||||
} |
|
||||||
|
|
||||||
public byte readByte() throws IOException |
|
||||||
{ |
|
||||||
return (byte) readToken(); |
|
||||||
} |
|
||||||
|
|
||||||
public char readChar() throws IOException |
|
||||||
{ |
|
||||||
return (char) readToken(); |
|
||||||
} |
|
||||||
|
|
||||||
public double readDouble() throws IOException |
|
||||||
{ |
|
||||||
return readNumber().doubleValue(); |
|
||||||
} |
|
||||||
|
|
||||||
public float readFloat() throws IOException |
|
||||||
{ |
|
||||||
return readNumber().floatValue(); |
|
||||||
} |
|
||||||
|
|
||||||
public void readFully(byte[] dst) throws IOException |
|
||||||
{ |
|
||||||
readFully(dst, 0, dst.length); |
|
||||||
} |
|
||||||
|
|
||||||
public void readFully(byte[] dst, int off, int len) throws IOException |
|
||||||
{ |
|
||||||
int total = 0; |
|
||||||
|
|
||||||
while (total < len) |
|
||||||
{ |
|
||||||
int r = read(dst, total, len - total); |
|
||||||
if (r == -1) |
|
||||||
{ |
|
||||||
throw new EOFException(); |
|
||||||
} |
|
||||||
|
|
||||||
total += r; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public int readInt() throws IOException |
|
||||||
{ |
|
||||||
return readNumber().intValue(); |
|
||||||
} |
|
||||||
|
|
||||||
public String readLine() throws IOException |
|
||||||
{ |
|
||||||
return readString(); |
|
||||||
} |
|
||||||
|
|
||||||
public long readLong() throws IOException |
|
||||||
{ |
|
||||||
return readNumber().longValue(); |
|
||||||
} |
|
||||||
|
|
||||||
public short readShort() throws IOException |
|
||||||
{ |
|
||||||
return readNumber().shortValue(); |
|
||||||
} |
|
||||||
|
|
||||||
public String readUTF() throws IOException |
|
||||||
{ |
|
||||||
return readString(Utils.UTF_8); |
|
||||||
} |
|
||||||
|
|
||||||
public int readUnsignedByte() throws IOException |
|
||||||
{ |
|
||||||
return readByte() & 0xFF; |
|
||||||
} |
|
||||||
|
|
||||||
public int readUnsignedShort() throws IOException |
|
||||||
{ |
|
||||||
return readShort() & 0xFFFF; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns a {@link Number}. |
|
||||||
*/ |
|
||||||
public Number readNumber() throws IOException |
|
||||||
{ |
|
||||||
int token = readToken(); |
|
||||||
|
|
||||||
if (!Utils.isNumber(token)) |
|
||||||
{ |
|
||||||
throw new IOException(); |
|
||||||
} |
|
||||||
|
|
||||||
return readNumber0(token); |
|
||||||
} |
|
||||||
|
|
||||||
private Number readNumber0(int token) throws IOException |
|
||||||
{ |
|
||||||
switch (token) |
|
||||||
{ |
|
||||||
case TypeCode.BYTE: |
|
||||||
return (int) readToBuffer(1).get(); |
|
||||||
case TypeCode.SHORT: |
|
||||||
return (int) readToBuffer(2).getShort(); |
|
||||||
case TypeCode.INT: |
|
||||||
return readToBuffer(4).getInt(); |
|
||||||
case TypeCode.LONG: |
|
||||||
return readToBuffer(8).getLong(); |
|
||||||
case TypeCode.FLOAT: |
|
||||||
return readToBuffer(4).getFloat(); |
|
||||||
case TypeCode.DOUBLE: |
|
||||||
return readToBuffer(8).getDouble(); |
|
||||||
|
|
||||||
case TypeCode.NUMBER: |
|
||||||
return readNumber0(); |
|
||||||
} |
|
||||||
if (Utils.isNegativeFixedNumber(token)) |
|
||||||
{ |
|
||||||
return TypeCode.EMBEDDED.INT_NEG_START - 1 - token; |
|
||||||
} |
|
||||||
else if (Utils.isPositiveFixedNumber(token)) |
|
||||||
{ |
|
||||||
return TypeCode.EMBEDDED.INT_POS_START + token; |
|
||||||
} |
|
||||||
|
|
||||||
throw new IOException("Unknown number. TypeCode: " + token); |
|
||||||
} |
|
||||||
|
|
||||||
private ByteBuffer readToBuffer(int count) throws IOException |
|
||||||
{ |
|
||||||
return ByteBuffer.wrap(readBytesFixed(count)); |
|
||||||
} |
|
||||||
|
|
||||||
private Number readNumber0() throws IOException |
|
||||||
{ |
|
||||||
StringBuilder buffer = new StringBuilder(); |
|
||||||
|
|
||||||
boolean decimal = false; |
|
||||||
|
|
||||||
int token = -1; |
|
||||||
while ((token = readToken()) != TypeCode.END) |
|
||||||
{ |
|
||||||
if (token == '.') |
|
||||||
{ |
|
||||||
decimal = true; |
|
||||||
} |
|
||||||
|
|
||||||
buffer.append((char) token); |
|
||||||
} |
|
||||||
|
|
||||||
try |
|
||||||
{ |
|
||||||
if (decimal) |
|
||||||
{ |
|
||||||
return new BigDecimal(buffer.toString()); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
return new BigInteger(buffer.toString()); |
|
||||||
} |
|
||||||
} |
|
||||||
catch (NumberFormatException err) |
|
||||||
{ |
|
||||||
throw new IOException("NumberFormatException", err); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public int skipBytes(int n) throws IOException |
|
||||||
{ |
|
||||||
return (int) skip(n); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns a byte-Array. |
|
||||||
*/ |
|
||||||
public byte[] readBytes() throws IOException |
|
||||||
{ |
|
||||||
int token = readToken(); |
|
||||||
|
|
||||||
return readBytes(token); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads and returns a {@link String}. |
|
||||||
*/ |
|
||||||
public String readString() throws IOException |
|
||||||
{ |
|
||||||
return readString(charset); |
|
||||||
} |
|
||||||
|
|
||||||
private String readString(String encoding) throws IOException |
|
||||||
{ |
|
||||||
return readString(readToken(), encoding); |
|
||||||
} |
|
||||||
|
|
||||||
private String readString(int token, String charset) throws IOException |
|
||||||
{ |
|
||||||
if (Utils.isFixedString(token)) |
|
||||||
{ |
|
||||||
int length = token - TypeCode.EMBEDDED.STR_START; |
|
||||||
return new String(readBytesFixed(length), charset); |
|
||||||
} |
|
||||||
return new String(readBytes(token), charset); |
|
||||||
} |
|
||||||
|
|
||||||
private byte[] readBytes(int token) throws IOException |
|
||||||
{ |
|
||||||
int length = readLength(token); |
|
||||||
return readBytesFixed(length); |
|
||||||
} |
|
||||||
|
|
||||||
private byte[] readBytesFixed(int count) throws IOException |
|
||||||
{ |
|
||||||
byte[] data = new byte[count]; |
|
||||||
readFully(data); |
|
||||||
return data; |
|
||||||
} |
|
||||||
|
|
||||||
private int readLength(int token) throws IOException |
|
||||||
{ |
|
||||||
StringBuilder buffer = new StringBuilder(); |
|
||||||
buffer.append((char) token); |
|
||||||
|
|
||||||
while ((token = readToken()) != TypeCode.LENGTH_DELIM) |
|
||||||
{ |
|
||||||
|
|
||||||
buffer.append((char) token); |
|
||||||
} |
|
||||||
|
|
||||||
return Integer.parseInt(buffer.toString()); |
|
||||||
} |
|
||||||
} |
|
@ -1,404 +0,0 @@ |
|||||||
package se.dimovski.rencode; |
|
||||||
|
|
||||||
import java.io.DataOutput; |
|
||||||
import java.io.FilterOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.OutputStream; |
|
||||||
import java.lang.reflect.Array; |
|
||||||
import java.nio.ByteBuffer; |
|
||||||
import java.util.Collection; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.SortedMap; |
|
||||||
import java.util.TreeMap; |
|
||||||
|
|
||||||
public class RencodeOutputStream extends FilterOutputStream implements DataOutput |
|
||||||
{ |
|
||||||
|
|
||||||
/** |
|
||||||
* The {@link String} charset. |
|
||||||
*/ |
|
||||||
private final String charset; |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a {@link RencodeOutputStream} with the default charset. |
|
||||||
*/ |
|
||||||
public RencodeOutputStream(OutputStream out) |
|
||||||
{ |
|
||||||
this(out, Utils.UTF_8); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a {@link RencodeOutputStream} with the given encoding. |
|
||||||
*/ |
|
||||||
public RencodeOutputStream(OutputStream out, String charset) |
|
||||||
{ |
|
||||||
super(out); |
|
||||||
|
|
||||||
if (charset == null) |
|
||||||
{ |
|
||||||
throw new NullPointerException("charset"); |
|
||||||
} |
|
||||||
|
|
||||||
this.charset = charset; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the charset that is used to encode {@link String}s. The default |
|
||||||
* value is UTF-8. |
|
||||||
*/ |
|
||||||
public String getCharset() |
|
||||||
{ |
|
||||||
return charset; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes an {@link Object}. |
|
||||||
*/ |
|
||||||
public void writeObject(Object value) throws IOException |
|
||||||
{ |
|
||||||
if (value == null) |
|
||||||
{ |
|
||||||
writeNull(); |
|
||||||
} |
|
||||||
else if (value instanceof byte[]) |
|
||||||
{ |
|
||||||
writeBytes((byte[]) value); |
|
||||||
} |
|
||||||
else if (value instanceof Boolean) |
|
||||||
{ |
|
||||||
writeBoolean((Boolean) value); |
|
||||||
|
|
||||||
} |
|
||||||
else if (value instanceof Character) |
|
||||||
{ |
|
||||||
writeChar((Character) value); |
|
||||||
|
|
||||||
} |
|
||||||
else if (value instanceof Number) |
|
||||||
{ |
|
||||||
writeNumber((Number) value); |
|
||||||
|
|
||||||
} |
|
||||||
else if (value instanceof String) |
|
||||||
{ |
|
||||||
writeString((String) value); |
|
||||||
|
|
||||||
} |
|
||||||
else if (value instanceof Collection<?>) |
|
||||||
{ |
|
||||||
writeCollection((Collection<?>) value); |
|
||||||
|
|
||||||
} |
|
||||||
else if (value instanceof Map<?, ?>) |
|
||||||
{ |
|
||||||
writeMap((Map<?, ?>) value); |
|
||||||
|
|
||||||
} |
|
||||||
else if (value instanceof Enum<?>) |
|
||||||
{ |
|
||||||
writeEnum((Enum<?>) value); |
|
||||||
|
|
||||||
} |
|
||||||
else if (value.getClass().isArray()) |
|
||||||
{ |
|
||||||
writeArray(value); |
|
||||||
|
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
writeCustom(value); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a null value |
|
||||||
*/ |
|
||||||
public void writeNull() throws IOException |
|
||||||
{ |
|
||||||
write(TypeCode.NULL); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Overwrite this method to write custom objects. The default implementation |
|
||||||
* throws an {@link IOException}. |
|
||||||
*/ |
|
||||||
protected void writeCustom(Object value) throws IOException |
|
||||||
{ |
|
||||||
throw new IOException("Cannot encode " + value); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes the given byte-Array |
|
||||||
*/ |
|
||||||
public void writeBytes(byte[] value) throws IOException |
|
||||||
{ |
|
||||||
writeBytes(value, 0, value.length); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes the given byte-Array |
|
||||||
*/ |
|
||||||
public void writeBytes(byte[] value, int offset, int length) throws IOException |
|
||||||
{ |
|
||||||
write(value, offset, length); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a boolean |
|
||||||
*/ |
|
||||||
public void writeBoolean(boolean value) throws IOException |
|
||||||
{ |
|
||||||
write(value ? TypeCode.TRUE : TypeCode.FALSE); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a char |
|
||||||
*/ |
|
||||||
public void writeChar(int value) throws IOException |
|
||||||
{ |
|
||||||
writeByte(value); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a byte |
|
||||||
*/ |
|
||||||
public void writeByte(int value) throws IOException |
|
||||||
{ |
|
||||||
write(TypeCode.BYTE); |
|
||||||
write(value); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a short |
|
||||||
*/ |
|
||||||
public void writeShort(int value) throws IOException |
|
||||||
{ |
|
||||||
write(TypeCode.SHORT); |
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Utils.SHORT_BYTES).putShort((short) value); |
|
||||||
write(buffer.array()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes an int |
|
||||||
*/ |
|
||||||
public void writeInt(int value) throws IOException |
|
||||||
{ |
|
||||||
write(TypeCode.INT); |
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Utils.INTEGER_BYTES).putInt(value); |
|
||||||
write(buffer.array()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a long |
|
||||||
*/ |
|
||||||
public void writeLong(long value) throws IOException |
|
||||||
{ |
|
||||||
write(TypeCode.LONG); |
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Utils.LONG_BYTES).putLong(value); |
|
||||||
write(buffer.array()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a float |
|
||||||
*/ |
|
||||||
public void writeFloat(float value) throws IOException |
|
||||||
{ |
|
||||||
write(TypeCode.FLOAT); |
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Utils.FLOAT_BYTES).putFloat(value); |
|
||||||
write(buffer.array()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a double |
|
||||||
*/ |
|
||||||
public void writeDouble(double value) throws IOException |
|
||||||
{ |
|
||||||
write(TypeCode.DOUBLE); |
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(Utils.DOUBLE_BYTES).putDouble(value); |
|
||||||
write(buffer.array()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a {@link Number} |
|
||||||
*/ |
|
||||||
public void writeNumber(Number num) throws IOException |
|
||||||
{ |
|
||||||
if (num instanceof Float) |
|
||||||
{ |
|
||||||
writeFloat(num.floatValue()); |
|
||||||
} |
|
||||||
else if (num instanceof Double) |
|
||||||
{ |
|
||||||
writeDouble(num.doubleValue()); |
|
||||||
} |
|
||||||
if (0 <= num.intValue() && num.intValue() < TypeCode.EMBEDDED.INT_POS_COUNT) |
|
||||||
{ |
|
||||||
write(TypeCode.EMBEDDED.INT_POS_START + num.intValue()); |
|
||||||
} |
|
||||||
else if (-TypeCode.EMBEDDED.INT_NEG_COUNT <= num.intValue() && num.intValue() < 0) |
|
||||||
{ |
|
||||||
write(TypeCode.EMBEDDED.INT_NEG_START - 1 - num.intValue()); |
|
||||||
} |
|
||||||
else if (Byte.MIN_VALUE <= num.intValue() && num.intValue() < Byte.MAX_VALUE) |
|
||||||
{ |
|
||||||
writeByte(num.byteValue()); |
|
||||||
} |
|
||||||
else if (Short.MIN_VALUE <= num.intValue() && num.intValue() < Short.MAX_VALUE) |
|
||||||
{ |
|
||||||
writeShort(num.shortValue()); |
|
||||||
} |
|
||||||
else if (Integer.MIN_VALUE <= num.longValue() && num.longValue() < Integer.MAX_VALUE) |
|
||||||
{ |
|
||||||
writeInt(num.intValue()); |
|
||||||
} |
|
||||||
else if (Long.MIN_VALUE <= num.longValue() && num.longValue() < Long.MAX_VALUE) |
|
||||||
{ |
|
||||||
writeLong(num.longValue()); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
String number = num.toString(); |
|
||||||
write(TypeCode.NUMBER); |
|
||||||
write(number.getBytes(charset)); |
|
||||||
write(TypeCode.END); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a {@link String} |
|
||||||
*/ |
|
||||||
public void writeString(String value) throws IOException |
|
||||||
{ |
|
||||||
int len = value.length(); |
|
||||||
if (len < TypeCode.EMBEDDED.STR_COUNT) |
|
||||||
{ |
|
||||||
write(TypeCode.EMBEDDED.STR_START + len); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
String lenString = Integer.toString(len); |
|
||||||
writeBytes(lenString.getBytes(charset)); |
|
||||||
write(TypeCode.LENGTH_DELIM); |
|
||||||
} |
|
||||||
|
|
||||||
writeBytes(value.getBytes(charset)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a {@link Collection}. |
|
||||||
*/ |
|
||||||
public void writeCollection(Collection<?> value) throws IOException |
|
||||||
{ |
|
||||||
boolean useEndToken = value.size() >= TypeCode.EMBEDDED.LIST_COUNT; |
|
||||||
if (useEndToken) |
|
||||||
{ |
|
||||||
write(TypeCode.LIST); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
write(TypeCode.EMBEDDED.LIST_START + value.size()); |
|
||||||
} |
|
||||||
|
|
||||||
for (Object element : value) |
|
||||||
{ |
|
||||||
writeObject(element); |
|
||||||
} |
|
||||||
|
|
||||||
if (useEndToken) |
|
||||||
{ |
|
||||||
write(TypeCode.END); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a {@link Map}. |
|
||||||
*/ |
|
||||||
public void writeMap(Map<?, ?> map) throws IOException |
|
||||||
{ |
|
||||||
if (!(map instanceof SortedMap<?, ?>)) |
|
||||||
{ |
|
||||||
map = new TreeMap<Object, Object>(map); |
|
||||||
} |
|
||||||
|
|
||||||
boolean untilEnd = map.size() >= TypeCode.EMBEDDED.DICT_COUNT; |
|
||||||
|
|
||||||
if (untilEnd) |
|
||||||
{ |
|
||||||
write(TypeCode.DICTIONARY); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
write(TypeCode.EMBEDDED.DICT_START + map.size()); |
|
||||||
} |
|
||||||
|
|
||||||
for (Map.Entry<?, ?> entry : map.entrySet()) |
|
||||||
{ |
|
||||||
writeObject(entry.getKey()); |
|
||||||
writeObject(entry.getValue()); |
|
||||||
} |
|
||||||
|
|
||||||
if (untilEnd) |
|
||||||
{ |
|
||||||
write(TypeCode.END); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes an {@link Enum}. |
|
||||||
*/ |
|
||||||
public void writeEnum(Enum<?> value) throws IOException |
|
||||||
{ |
|
||||||
writeString(value.name()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes an array |
|
||||||
*/ |
|
||||||
public void writeArray(Object value) throws IOException |
|
||||||
{ |
|
||||||
int length = Array.getLength(value); |
|
||||||
boolean useEndToken = length >= TypeCode.EMBEDDED.LIST_COUNT; |
|
||||||
if (useEndToken) |
|
||||||
{ |
|
||||||
write(TypeCode.LIST); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
write(TypeCode.EMBEDDED.LIST_START + length); |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) |
|
||||||
{ |
|
||||||
writeObject(Array.get(value, i)); |
|
||||||
} |
|
||||||
|
|
||||||
if (useEndToken) |
|
||||||
{ |
|
||||||
write(TypeCode.END); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes the given {@link String} |
|
||||||
*/ |
|
||||||
public void writeBytes(String value) throws IOException |
|
||||||
{ |
|
||||||
writeString(value); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes the given {@link String} |
|
||||||
*/ |
|
||||||
public void writeChars(String value) throws IOException |
|
||||||
{ |
|
||||||
writeString(value); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes an UTF encoded {@link String} |
|
||||||
*/ |
|
||||||
public void writeUTF(String value) throws IOException |
|
||||||
{ |
|
||||||
writeBytes(value.getBytes(Utils.UTF_8)); |
|
||||||
} |
|
||||||
} |
|
@ -1,47 +0,0 @@ |
|||||||
package se.dimovski.rencode; |
|
||||||
|
|
||||||
public class TypeCode |
|
||||||
{ |
|
||||||
// The bencode 'typecodes' such as i, d, etc have been
|
|
||||||
// extended and relocated on the base-256 character set.
|
|
||||||
public static final char LIST = 59; |
|
||||||
public static final char DICTIONARY = 60; |
|
||||||
public static final char NUMBER = 61; |
|
||||||
public static final char BYTE = 62; |
|
||||||
public static final char SHORT = 63; |
|
||||||
public static final char INT = 64; |
|
||||||
public static final char LONG = 65; |
|
||||||
public static final char FLOAT = 66; |
|
||||||
public static final char DOUBLE = 44; |
|
||||||
public static final char TRUE = 67; |
|
||||||
public static final char FALSE = 68; |
|
||||||
public static final char NULL = 69; |
|
||||||
public static final char END = 127; |
|
||||||
public static final char LENGTH_DELIM = ':'; |
|
||||||
|
|
||||||
/* |
|
||||||
* TypeCodes with embedded values/lengths |
|
||||||
*/ |
|
||||||
public static class EMBEDDED |
|
||||||
{ |
|
||||||
// Positive integers
|
|
||||||
public static final int INT_POS_START = 0; |
|
||||||
public static final int INT_POS_COUNT = 44; |
|
||||||
|
|
||||||
// Negative integers
|
|
||||||
public static final int INT_NEG_START = 70; |
|
||||||
public static final int INT_NEG_COUNT = 32; |
|
||||||
|
|
||||||
// Dictionaries
|
|
||||||
public static final int DICT_START = 102; |
|
||||||
public static final int DICT_COUNT = 25; |
|
||||||
|
|
||||||
// Strings
|
|
||||||
public static final int STR_START = 128; |
|
||||||
public static final int STR_COUNT = 64; |
|
||||||
|
|
||||||
// Lists
|
|
||||||
public static final int LIST_START = STR_START + STR_COUNT; |
|
||||||
public static final int LIST_COUNT = 64; |
|
||||||
} |
|
||||||
} |
|
@ -1,74 +0,0 @@ |
|||||||
package se.dimovski.rencode; |
|
||||||
|
|
||||||
public class Utils |
|
||||||
{ |
|
||||||
// Character Encodings
|
|
||||||
public final static String UTF_8 = "UTF-8"; |
|
||||||
public final static String ISO_8859 = "ISO-8859-1"; |
|
||||||
|
|
||||||
// Byte-lengths for types
|
|
||||||
public static final int SHORT_BYTES = Short.SIZE / Byte.SIZE; |
|
||||||
public static final int INTEGER_BYTES = Integer.SIZE / Byte.SIZE; |
|
||||||
public static final int LONG_BYTES = Long.SIZE / Byte.SIZE; |
|
||||||
public static final int FLOAT_BYTES = Float.SIZE / Byte.SIZE; |
|
||||||
public static final int DOUBLE_BYTES = Double.SIZE / Byte.SIZE; |
|
||||||
|
|
||||||
// Maximum length of integer when written as base 10 string.
|
|
||||||
public static final int MAX_INT_LENGTH = 64; |
|
||||||
|
|
||||||
private static boolean tokenInRange(int token, int start, int count) |
|
||||||
{ |
|
||||||
return start <= token && token < (start + count); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isNumber(int token) |
|
||||||
{ |
|
||||||
switch (token) |
|
||||||
{ |
|
||||||
case TypeCode.NUMBER: |
|
||||||
case TypeCode.BYTE: |
|
||||||
case TypeCode.SHORT: |
|
||||||
case TypeCode.INT: |
|
||||||
case TypeCode.LONG: |
|
||||||
case TypeCode.FLOAT: |
|
||||||
case TypeCode.DOUBLE: |
|
||||||
return true; |
|
||||||
} |
|
||||||
return isFixedNumber(token); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isFixedNumber(int token) |
|
||||||
{ |
|
||||||
return isPositiveFixedNumber(token) || isNegativeFixedNumber(token); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isPositiveFixedNumber(int token) |
|
||||||
{ |
|
||||||
return tokenInRange(token, TypeCode.EMBEDDED.INT_POS_START, TypeCode.EMBEDDED.INT_POS_COUNT); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isNegativeFixedNumber(int token) |
|
||||||
{ |
|
||||||
return tokenInRange(token, TypeCode.EMBEDDED.INT_NEG_START, TypeCode.EMBEDDED.INT_NEG_COUNT); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isFixedList(int token) |
|
||||||
{ |
|
||||||
return tokenInRange(token, TypeCode.EMBEDDED.LIST_START, TypeCode.EMBEDDED.LIST_COUNT); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isFixedDictionary(int token) |
|
||||||
{ |
|
||||||
return tokenInRange(token, TypeCode.EMBEDDED.DICT_START, TypeCode.EMBEDDED.DICT_COUNT); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isFixedString(int token) |
|
||||||
{ |
|
||||||
return tokenInRange(token, TypeCode.EMBEDDED.STR_START, TypeCode.EMBEDDED.STR_COUNT); |
|
||||||
} |
|
||||||
|
|
||||||
public static boolean isDigit(int token) |
|
||||||
{ |
|
||||||
return '0' <= token && token <= '9'; |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue